首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

spring security3.2.0容易配置

2013-06-26 
spring security3.2.0简单配置?xml version1.0 encodingUTF-8?web-app xmlns:xsihttp://www.w3

spring security3.2.0简单配置
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"id="WebApp_ID" version="3.0"><display-name>spring_security</display-name><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:/applicationContext*.xml</param-value></context-param><!-- 指定Log4J配置文件位置 --><context-param><param-name>log4jConfigLocation</param-name><param-value>/WEB-INF/log4j.properties</param-value></context-param><listener> <!-- 负责加载 log4j监听器 --> <listener-class> org.springframework.web.util.Log4jConfigListener </listener-class></listener><listener><!-- 负责加载 classpath:applicationContext --><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><listener> <!-- 负责加载会话监听器事件 --> <listener-class> org.springframework.security.web.session.HttpSessionEventPublisher </listener-class> </listener><!-- 负责加载spring security过滤器 --><filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class> org.springframework.web.filter.DelegatingFilterProxy </filter-class> <init-param> <param-name>target-class</param-name> <param-value>org.springframework.security.web.FilterChainProxy</param-value> </init-param></filter><filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern></filter-mapping><filter><filter-name>setCharacterEncoding</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><!-- filtered type --><filter-mapping><filter-name>setCharacterEncoding</filter-name><url-pattern>/*</url-pattern></filter-mapping><!-- 系统欢迎界面 --><welcome-file-list> <welcome-file>login.jsp</welcome-file></welcome-file-list></web-app>

?


该文件用于在容器启动时自动加载spring security的过滤器,用来过滤所有的用户请求


2)applicationContext-security.xml

<?xml version="1.0" encoding="UTF-8"?><!-- - Application context containing authentication, channel - security and web URI beans. - - Only used by "filter" artifact. - --><b:beans xmlns="http://www.springframework.org/schema/security"xmlns:b="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/security         http://www.springframework.org/schema/security/spring-security-3.1.xsd">        <!-- 不要过滤图片等静态资源 -->    <http pattern="/**/*.jpg" security="none"/>    <http pattern="/**/*.png" security="none"/>    <http pattern="/**/*.gif" security="none"/>    <http pattern="/**/*.css" security="none"/>    <http pattern="/**/*.js" security="none"/>        <!-- 这个元素用来在你的应用程序中启用基于安全的注解     <global-method-security  pre-post-annotations="enabled" access-decision-manager-ref="myAccessDecisionManager"/>    -->        <!-- 配置页面访问权限 --><http auto-config='true' access-denied-page="/accessDenied.jsp">    <!-- 登陆页面和忘记密码页面不过滤 --><intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /><intercept-url pattern="/forgotpassword.jsp"/><form-login login-page="/login.jsp"authentication-failure-url="/login.jsp?error=true"default-target-url="/index.jsp" always-use-default-target='true' /><!-- "记住我"功能,采用持久化策略(将用户的登录信息存放在数据库表中) --><remember-me data-source-ref="dataSource" /><!-- 实现免登陆验证 --><!-- 只能登陆一次 --><session-managementsession-authentication-error-url="/402.jsp" invalid-session-url="/sessionTimeout.jsp"><concurrency-control max-sessions="1"error-if-maximum-exceeded="true" /></session-management><logout/><!-- 增加一个自定义的filter,放在FILTER_SECURITY_INTERCEPTOR之前,实现用户、角色、权限、资源的数据库管理。 11/3/23 --><custom-filter ref="myFilter" before="FILTER_SECURITY_INTERCEPTOR"/> </http><!-- 将用户信息硬编码在xml文件中 --><!-- <authentication-manager> <authentication-provider> <user-service> <user name="admin" password="admin" authorities="ROLE_ADMIN,ROLE_USER" /> <user name="user" password="user" authorities="ROLE_USER" /> </user-service> </authentication-provider> </authentication-manager> --><!-- 数据中查找用户 <authentication-manager alias="myAuthenticationManager"><authentication-provider><jdbc-user-service data-source-ref="dataSource"users-by-username-query="select username,password,status as enabled                               from user                              where username=?"authorities-by-username-query="select u.username,r.name as authority                                             from user u                                     join user_role ur                                    on u.id=ur.user_id                                      join role r                                    on r.id=ur.role_id                                     where u.username=?" /></authentication-provider></authentication-manager>--><!-- 实现了UserDetailsService的Bean --><!-- 注意能够为authentication-manager 设置alias别名 --><authentication-manager alias="myAuthenticationManager"><authentication-provider user-service-ref="myUserDetailService">   <password-encoder ref="passwordEncoder"><salt-source user-property="username" /></password-encoder> </authentication-provider></authentication-manager><!-- 一个自定义的filter,必须包含authenticationManager, accessDecisionManager,securityMetadataSource三个属性。 --><b:bean id="myFilter"ref="myAuthenticationManager"></b:property><!-- 用户是否拥有所请求资源的权限 --><b:property name="accessDecisionManager" ref="myAccessDecisionManager"></b:property><!-- 资源与权限对应关系 --><b:property name="securityMetadataSource" ref="mySecurityMetadataSource"></b:property></b:bean></b:beans>

?

该文件就是spring security最主要的配置文件,该文件中配置了资源的访问权限,以及用户的来源。我们看到是从UserDetailsService来获取用户信息,然后再经过自定义过滤器myFilter来实现用户权限资源的管理。myFilter依赖于这三个类,authenticationManager,accessDecisionManager,securityMetadataSource,具体这三个类的Bean在下面这个配置中有配置,这三个Bean的作用在配置文件中有说明,这里就不在赘述。我用了两个配置文件目的就是把,Bean的配置放在applicationContext-common-business.xml文件中,spring security的配置文件只有引用,这样看起来结构更加清晰。也就是说下面的配置文件类似魏生产Bean的工厂。



3)applicationContext-common-business.xml

<?xml version="1.0" encoding="UTF-8"?><!-- - Application context containing business beans. - - Used by all artifacts. - --><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd       http://www.springframework.org/schema/context        http://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- 加载数据库资源文件 --><context:property-placeholder location="classpath:dbconn.properties" /><!-- 扫描包,应用Spring的注解 --><context:component-scan base-package="com.spring.security.dao" /><!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 --><beanp:prefix="/WEB-INF/security/"p:suffix=".jsp" p:contentType="text/html;charset=UTF-8" /><!-- 定义使用springframework数据源实现 --><bean id="dataSource"value="${jdbc.driverClassName}" /><!-- 指定连接数据库的URl --><property name="url" value="${jdbc.url}" /><!-- 指定连接数据库的用户名 --><property name="username" value="${jdbc.username}" /><!-- 指定连接数据库的密码 --><property name="password" value="${jdbc.password}" /></bean><!-- 操作数据库的模板类 --><bean id="jdbcTemplate" ref="userCache"/></bean><!-- 用户的密码加密或解密 --><bean id="passwordEncoder"/><!-- 启用用户的缓存功能 --><bean id="userCache"ref="userEhCache" /></bean><bean id="userEhCache" value="userCache" /><property name="cacheManager" ref="cacheManager" /></bean><bean id="cacheManager"/><!-- 设置权限的前缀为AUTH_,而不是ROLE_。 <bean id="roleVoter" value="ROLE_"/>    </bean>    -->        <!-- 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源。 --><bean id="myAccessDecisionManager" ref="resourcesDao"></constructor-arg></bean></beans>

?
所有Bean类的配置



4)数据库资源文件dbconn.properties

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/javaee
jdbc.username=root
jdbc.password=123


5)缓存配置文件ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">    <diskStore path="java.io.tmpdir"/>    <defaultCache            maxElementsInMemory="10000"            eternal="false"            timeToIdleSeconds="120"            timeToLiveSeconds="120"            overflowToDisk="true"            maxElementsOnDisk="10000000"            diskPersistent="false"            diskExpiryThreadIntervalSeconds="120"            memoryStoreEvictionPolicy="LRU"            />                <!-- set Category's Data expiration policies -->    <cache name="org.qiujy.domain.cachedemo.Category"    maxElementsInMemory="100"    eternal="true"    timeToIdleSeconds="0"    timeToLiveSeconds="0"    overflowToDisk="false"    />        <!-- set Category's productes Data expiration policies -->    <cache name="org.qiujy.domain.cachedemo.Category.products"    maxElementsInMemory="500"    eternal="false"    timeToIdleSeconds="300"    timeToLiveSeconds="600"    overflowToDisk="true"    />        <!-- set Product's Data expiration policies -->    <cache name="org.qiujy.domain.cachedemo.Product"    maxElementsInMemory="500"    eternal="false"    timeToIdleSeconds="300"    timeToLiveSeconds="600"    overflowToDisk="true"    />        <!-- set Default Data expiration policies -->    <cache name="org.hibernate.cache.StandardQueryCache"        maxElementsInMemory="50"       eternal="false"        timeToIdleSeconds="3600"        timeToLiveSeconds="7200"        overflowToDisk="true"    />        <!-- set customerQueries Data expiration policies-->    <cache name="myCacheRegion"        maxElementsInMemory="1000"        eternal="false"        timeToIdleSeconds="300"        timeToLiveSeconds="600"        overflowToDisk="true"    />        <!-- spring security's cache config. sparta 10/6/20 10:56 -->    <cache        name="userCache"        maxElementsInMemory="100"        eternal="false"        timeToIdleSeconds="600"        timeToLiveSeconds="3600"        overflowToDisk="true"    />        <!-- spring security's acls cache config. sparta 10/6/22 15:41 -->    <cache    name="aclCache"    maxElementsInMemory="1000"    eternal="false"    timeToIdleSeconds="600"    timeToLiveSeconds="3600"    overflowToDisk="true"/>        </ehcache>

?

?6)myFilter过滤用户请求

? (1)MyFilterSecurityInterceptor.java

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptorimplements Filter {//与applicationContext-security.xml里的myFilter的属性securityMetadataSource对应,  //其他的两个组件,已经在AbstractSecurityInterceptor定义  @Autowiredprivate FilterInvocationSecurityMetadataSource securityMetadataSource;public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {FilterInvocation fi = new FilterInvocation(request, response, chain);invoke(fi);}public Class<? extends Object> getSecureObjectClass() {return FilterInvocation.class;}public void invoke(FilterInvocation fi) throws IOException,ServletException {// object为FilterInvocation对象  //super.beforeInvocation(fi);源码  //1.获取请求资源的权限  //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);  //2.是否拥有权限  //this.accessDecisionManager.decide(authenticated, object, attributes);  InterceptorStatusToken token = super.beforeInvocation(fi);try {fi.getChain().doFilter(fi.getRequest(), fi.getResponse());} finally {super.afterInvocation(token, null);}}@Overridepublic SecurityMetadataSource obtainSecurityMetadataSource() {return this.securityMetadataSource;}public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {this.securityMetadataSource = securityMetadataSource;}public void destroy() {}public void init(FilterConfig filterconfig) throws ServletException {}}

?核心的InterceptorStatusToken token = super.beforeInvocation(fi);会调用我们定义的accessDecisionManager:decide(Object object)和securityMetadataSource

:getAttributes(Object object)方法。

? (2)MyInvocationSecurityMetadataSourceService.java

public class MyInvocationSecurityMetadataSourceService implementsFilterInvocationSecurityMetadataSource {private ResourcesDao resourcesDao;// resourceMap及为key-url,value-Collection<ConfigAttribute>,资源权限对应Mapprivate static Map<String, Collection<ConfigAttribute>> resourceMap = null;public MyInvocationSecurityMetadataSourceService(ResourcesDao resourcesDao) {this.resourcesDao = resourcesDao;System.out.println("加载MyInvocationSecurityMetadataSourceService..."+ resourcesDao);loadResourceDefine();}// 加载所有资源与权限的关系private void loadResourceDefine() {if (resourceMap == null) {resourceMap = new HashMap<String, Collection<ConfigAttribute>>();List<Resource> resources = resourcesDao.findAll();//加载资源对应的权限for (Resource resource : resources) {Collection<ConfigAttribute> auths = resourcesDao.loadRoleByResource(resource.getRes_string());System.out.println("权限=" + auths);resourceMap.put(resource.getRes_string(), auths);}}}//加载所有资源与权限的关系@Overridepublic Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException {// object是一个URL,被用户请求的urlString requestUrl = ((FilterInvocation) object).getRequestUrl();System.out.println("requestUrl is " + requestUrl);int firstQuestionMarkIndex = requestUrl.indexOf("?");if (firstQuestionMarkIndex != -1) {requestUrl = requestUrl.substring(0, firstQuestionMarkIndex);}if (resourceMap == null) {loadResourceDefine();}//Iterator<String> ite = resourceMap.keySet().iterator();while (ite.hasNext()) {String resURL = ite.next();if (resURL.equals(requestUrl)) {return resourceMap.get(resURL);}}return null;}@Overridepublic boolean supports(Class<?> arg0) {// TODO Auto-generated method stubreturn true;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {// TODO Auto-generated method stubreturn null;}}

?


?? (3)MyAccessDecisionManager.java

public class MyAccessDecisionManager implements AccessDecisionManager {@Overridepublic void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes)throws AccessDeniedException, InsufficientAuthenticationException {if (configAttributes == null) {return;}//所请求的资源拥有的权限(一个资源对多个权限)Iterator<ConfigAttribute> ite = configAttributes.iterator();while (ite.hasNext()) {ConfigAttribute ca = ite.next();//访问所请求资源所需要的权限 String needRole = ((SecurityConfig) ca).getAttribute();System.out.println("needRole is " + needRole); // ga 为用户所被赋予的权限。 needRole 为访问相应的资源应该具有的权限。for (GrantedAuthority ga : authentication.getAuthorities()) {if (needRole.trim().equals(ga.getAuthority().trim())) {return;}}}//没有权限throw new AccessDeniedException("没有权限访问!");}@Overridepublic boolean supports(ConfigAttribute arg0) {// TODO Auto-generated method stubreturn true;}@Overridepublic boolean supports(Class<?> arg0) {// TODO Auto-generated method stubreturn true;}}

?? (4)MyUserDetailService.java

/* * 该类的主要作用是为Spring Security提供一个经过用户认证后的UserDetails。 *该UserDetails包括用户名、密码、是否可用、是否过期等信息。 */public class MyUserDetailService implements UserDetailsService {@Autowiredprivate UserInfoDao userInfoDao;@Autowiredprivate UserCache userCache;@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException {System.out.println("username is :" + username);Users user = null;try {user = this.userInfoDao.findByName(username);System.out.println(user);} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}// 获得用户权限Collection<GrantedAuthority> auths = userInfoDao.loadUserAuthorityByName(username);boolean enables = true;// 账户过期否boolean accountNonExpired = true;// 证书过期否boolean credentialsNonExpired = true;// 账户锁定否boolean accountNonLocked = true;// 封装成spring security的userUser userdetail = new User(username, user.getPassword(), enables,accountNonExpired, credentialsNonExpired, accountNonLocked,auths);for (GrantedAuthority s : auths) {s.getAuthority();}System.out.println(auths);return userdetail;}public UserInfoDao getUserInfoDao() {return userInfoDao;}public void setUserInfoDao(UserInfoDao userInfoDao) {this.userInfoDao = userInfoDao;}//设置用户缓存功能。public UserCache getUserCache() {return userCache;}public void setUserCache(UserCache userCache) {this.userCache = userCache;}}

?7)Dao组件

? 1)ResourcesDao.java

@Repositorypublic class ResourcesDao {@Autowiredprivate JdbcTemplate jdbcTemplate;private static final Log log = LogFactory.getLog(UserInfoDao.class);public ResourcesDao() {System.out.println("加载ResourcesDao..." + jdbcTemplate);}public List<Resource> findAll() {try {List<Resource> resourceList = new ArrayList<Resource>();List<Map<String, Object>> resources = jdbcTemplate.queryForList("select * from resc");for (Map<String, Object> map : resources) {Resource r = new Resource();r.setId(Integer.valueOf(map.get("id").toString()));r.setName(map.get("name").toString());r.setRes_type(map.get("res_type").toString());r.setRes_string(map.get("res_string").toString());r.setDescn(map.get("descn").toString());resourceList.add(r);}return resourceList;} catch (RuntimeException re) {log.error("find all resource failed " + re);throw re;}}// 加载资源与对应的权限public Collection<ConfigAttribute> loadRoleByResource(String url) {try {String sql = "select ro.name as role,re.res_string as url "+ "from role ro join resc_role rr on ro.id=rr.role_id "+ "join resc re on re.id=rr.resc_id "+ "where re.res_string='" + url + "'";List<Map<String, Object>> authList = jdbcTemplate.queryForList(sql);Collection<ConfigAttribute> auths = new ArrayList<ConfigAttribute>();for(Map<String, Object> map:authList){ConfigAttribute auth = new SecurityConfig(map.get("role").toString());auths.add(auth);}return auths;} catch (RuntimeException re) {log.error("find roles by url failed " + re);throw re;}}}

? ?2)UserInfoDao.java

@SuppressWarnings("deprecation")@Repositorypublic class UserInfoDao {@Autowiredprivate JdbcTemplate jdbcTemplate;private static final Log log = LogFactory.getLog(UserInfoDao.class);public UserInfoDao(){loadSource();System.out.println("加载UserInfoDao..." + jdbcTemplate);}public Users findByName(String username) throws SQLException {String sql = "select * from user where username='" + username + "'";RowMapper<Users> mapper = new RowMapper<Users>() {@Overridepublic Users mapRow(ResultSet rs, int rowNum) throws SQLException {Users user = new Users();user.setId((int) rs.getLong("id"));user.setUsername(rs.getString("username"));user.setPassword(rs.getString("password"));user.setStatus(rs.getInt("status"));user.setDescribtion(rs.getString("descn"));return user;}};System.out.println("怎么没有呢?");Users user = jdbcTemplate.queryForObject(sql, mapper);return user;}// 通过用户名获得权限集合public Collection<GrantedAuthority> loadUserAuthorityByName(String username) {try{List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();List<String> authsList = loadUserAuthorities(username);for(String roleName:authsList){GrantedAuthorityImpl authority = new GrantedAuthorityImpl(roleName);auths.add(authority);}return auths;}catch(RuntimeException re){log.error("" + re);throw re;}}// 获取权限列表public List<String> loadUserAuthorities(String username) {try {String sql = "select r.name as authority "+ "from user u join user_role ur on u.id=ur.user_id "+ "join role r on r.id=ur.role_id " + "where u.username='"+ username + "'";List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);List<String> roles = new ArrayList<>();for (Map<String, Object> map : list) {roles.add((String) map.get("authority"));}return roles;} catch (RuntimeException re) {log.error("find by authorities by username failed." + re);throw re;}}private void loadSource(){System.out.println("加载资源");}}

?还有一些Domain类在此就省略不提了。以上就是笔者经过一个星期搞出来的,总算是能用了。

?

三.说明

?

1)容器启动(MySecurityMetadataSource:loadResourceDefine加载系统资源与权限列表)
2)用户发出请求
3)过滤器拦截(MySecurityFilter:doFilter)
4)取得请求资源所需权限(MySecurityMetadataSource:getAttributes)
5)匹配用户拥有权限和请求权限(MyAccessDecisionManager:decide),如果用户没有相应的权限,

执行第6步,否则执行第7步。
6)登录
7)验证并授权(MyUserDetailServiceImpl:loadUserByUsername)
8)重复4,5

注:该流程为复制其他博客主的

?

四.结束

最后衷心的希望大家在程序员这条路上走得更远,更久,为我国软件行业做出一些贡献。

?

热点排行