Spring Security 可动态受权RBAC权限模块实践

Spring Security 可动态授权RBAC权限模块实践

 

Spring Security 可动态授权RBAC权限模块实践

分类: 项目总结 Java Web Security 182人阅读 评论(0) 收藏 举报

先在web.xml 中配置一个过滤器(必须在Struts的过滤器之前)

 

[html] view plaincopy
  1. <filter>  
  2.     <filter-name>springSecurityFilterChain</filter-name>  
  3.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  4. </filter>  
  5. <filter-mapping>  
  6.     <filter-name>springSecurityFilterChain</filter-name>  
  7.     <url-pattern>/*</url-pattern>  
  8. </filter-mapping>  

然后就是编写Spring安全的配置文件applicationContext-security.xml并配置到Spring解析的路径下

 

Spring Security主要做两件事,一件是认证,一件是授权。

 

认证

当用户访问受保护的信息却没有登录获得认证时,框架会自动将请求跳转到登录页面


在http标签中的
[html] view plaincopy
  1. <form-login login-page="/page/login.jsp" />  

配置。且该登录页面必须是不被拦截的。故要配置上
[html] view plaincopy
  1. <intercept-url pattern="/page/login.jsp" filters="none" />  


Web项目的认证如果在HTTP标签中配置了auto-config="true",框架就会自动的配置多8?个拦截器。 默认表单登录认证的是FORM_LOGIN_FILTER拦截器,我们可以直接写自定义的UserDetailsService,在这个类中实现方法UserDetails loadUserByUsername(String username),从数据库获取用户信息,以及其拥有的角色。
[java] view plaincopy
  1. @Service("myUserDetailsService")  
  2. public class MyUserDetailsServiceImpl extends BaseService implements UserDetailsService {  
  3. @Resource  
  4. private UserDao userDao;  
  5.   
  6.   
  7. public UserDetails loadUserByUsername(String username)  
  8. throws UsernameNotFoundException, DataAccessException {  
  9.   
  10. User user = userDao.getUserByUsername(username);  
  11. List<Role> roles = user.getRoles();  
  12. Collection<GrantedAuthority> authorities = new LinkedList<GrantedAuthority>();  
  13.   
  14. for (Role role : roles) {  
  15. authorities.add(new GrantedAuthorityImpl(role.getCode()));  
  16. }  
  17.   
  18. UserDetails userDetails = new org.springframework.security.core.userdetails.User(username,user.getPassword(),Constants.STATE_VALID.equals(user.getState()),true,true,true,authorities);  
  19.   
  20. return userDetails;  
  21. }  
  22.   
  23.   
  24. }  

配置在

 

[html] view plaincopy
  1. <authentication-manager alias="myAuthenticationManager">  
  2.     <authentication-provider user-service-ref="myUserDetailsService">  
  3.         <password-encoder hash="md5" />  
  4.     </authentication-provider>  
  5. </authentication-manager>  

如果需要在登录的时候,在HTTP SESSION中配置做些操作的。就得配置自定义的FORM_LOGIN_FILTER了

 

在HTTP标签中加入
[html] view plaincopy
  1. <custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />  

并配置
[html] view plaincopy
  1. <!-- 访问控制验证器Authority -->  
  2. <beans:bean id="securityFilter"  
  3. class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
  4. <beans:property name="authenticationManager" ref="myAuthenticationManager" />  
  5. <beans:property name="accessDecisionManager"  
  6. ref="affirmativeBasedAccessDecisionManager" />  
  7. <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>  
  8. </beans:bean>  



MyUsernamePasswordAuthenticationFilter 类是这么写的
[java] view plaincopy
  1. public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{  
  2. public static final String USERNAME = "username";  
  3. public static final String PASSWORD = "password";  
  4.   
  5. @Resource  
  6. private LoginService loginService;  
  7. private UserLoginFormBean userLoginFormBean = new UserLoginFormBean();  
  8.   
  9. @Resource  
  10. private LogService logService;  
  11.   
  12.   
  13.   
  14. @Override  
  15. public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {  
  16. String username = obtainUsername(request);  
  17. String password = obtainPassword(request);  
  18.   
  19. HttpSession session = request.getSession();  
  20.   
  21. userLoginFormBean.setUsername(obtainUsername(request));  
  22. userLoginFormBean.setPassword(obtainPassword(request));  
  23.   
  24. User user = loginService.login(userLoginFormBean);  
  25. session.setAttribute(Constants.SESSION_USER, user);  
  26.   
  27.   
  28. Log log = new Log(user,getIpAddr(request),"用户登录"null);  
  29. logService.add(log);  
  30.   
  31. //UsernamePasswordAuthenticationToken实现 Authentication  
  32. UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);  
  33. // Place the last username attempted into HttpSession for views  
  34.   
  35. // 允许子类设置详细属性  
  36.         setDetails(request, authRequest);  
  37.   
  38.         // 运行UserDetailsService的loadUserByUsername 再次封装Authentication  
  39. return this.getAuthenticationManager().authenticate(authRequest);  
  40. }  
  41.   
  42.   
  43.   
  44. }  

getAuthenticationManager().authenticate(authRequest)是为了让UserDetailService提供Detailed的信息并认证

 

授权

在授权时,系统默认通过FILTER_SECURITY_INTERCEPTOR认证。


如需自定义授权拦截器,我们在HTTP中在默认授权拦截器前配置了自定义的拦截器

[html] view plaincopy
  1. <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />  

本平台采用基于请求URL地址的验证方式


securityFilter的配置如下
[html] view plaincopy
  1. <!-- 访问控制验证器Authority -->  
  2. <beans:bean id="securityFilter"  
  3.     class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
  4.     <beans:property name="authenticationManager" ref="myAuthenticationManager" />  
  5.     <beans:property name="accessDecisionManager"  
  6.         ref="affirmativeBasedAccessDecisionManager" />  
  7.     <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>  
  8. </beans:bean>  


采用默认的自定义的也是采用Spring默认的FilterSecurityInterceptor拦截器,accessDecisionManager也采用的是框架提供的affirmativeBasedAccessDecisionManager
采用投票者来判断是否授权。
[html] view plaincopy
  1. <beans:bean id="affirmativeBasedAccessDecisionManager"  
  2.         class="org.springframework.security.access.vote.AffirmativeBased">  
  3.         <beans:property name="decisionVoters" ref="roleDecisionVoter" />  
  4.     </beans:bean>  
  5.       
  6.     <beans:bean name="roleDecisionVoter"  
  7.         class="org.springframework.security.access.vote.RoleVoter" />  
  8.           
  9. <beans:bean id="mySecurityMetadataSource"  
  10.         class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">  
  11.         <beans:constructor-arg  
  12.             type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />  
  13.         <beans:constructor-arg type="java.util.LinkedHashMap"  
  14.             ref="securityRequestMapFactoryBean" />  
  15. </beans:bean>  


SecurityMetadataSource也是ss web框架提供的DefaultFilterInvocationSecurityMetadataSource,只是初始化参数中,一个选择antUrl匹配,还是正则匹配,另一个是提供自定义的通过securityRequestMapFactoryBean。在后者是一个LinkedHashMap<RequestKey, Collection<ConfigAttribute>>类型,就是每一个URL匹配模式,所需要角色的集合。
[java] view plaincopy
  1. @Service("securityRequestMapFactoryBean")  
  2. public class SecurityRequestMapFactoryBean extends  
  3.         LinkedHashMap<RequestKey, Collection<ConfigAttribute>> {  
  4.       
  5.     @Resource  
  6.     private ModuleDao moduleDao;  
  7.       
  8.     @PostConstruct  
  9.     public void loadSecurityInfos(){  
  10.         List<Module> modules = moduleDao.getAll(new Module());  
  11. //      List<Role> roles = roleDao.getAll(new Role());  
  12.         for (Module module : modules) {  
  13.             RequestKey requestKey = new RequestKey(module.getPageUrl());  
  14.             Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();  
  15.             for (final Role role : module.getRoles()) {  
  16.                 configAttributes.add(new ConfigAttribute() {  
  17.                     public String getAttribute() {  
  18.                         return role.getCode();  
  19.                     }  
  20.                 });  
  21.             }  
  22.             this.put(requestKey, configAttributes);  
  23.         }  
  24.     }  
  25.       
  26. }  

PS: 最终的件applicationContext-security.xml配置文件

 

 

[html] view plaincopy
  1. <pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>  
  2. <beans:beans xmlns="http://www.springframework.org/schema/security"  
  3.     xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  5.                         http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">  
  6.   
  7.     <http auto-config="true">  
  8.         <intercept-url pattern="/page/login.jsp" filters="none" />  
  9.         <intercept-url pattern="/LoginAction*" filters="none" />  
  10.         <intercept-url pattern="/common/**" filters="none" />  
  11.         <intercept-url pattern="/css/**" filters="none" />  
  12.         <intercept-url pattern="/common/**" filters="none" />  
  13.         <intercept-url pattern="/images/**" filters="none" />  
  14.         <intercept-url pattern="/js/**" filters="none" />  
  15.   
  16.         <form-login login-page="/page/login.jsp" />  
  17.         <custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />  
  18.         <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />  
  19.     </http>  
  20.   
  21.     <!-- 访问控制验证器Authority -->  
  22.     <beans:bean id="securityFilter"  
  23.         class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">  
  24.         <beans:property name="authenticationManager" ref="myAuthenticationManager" />  
  25.         <beans:property name="accessDecisionManager"  
  26.             ref="affirmativeBasedAccessDecisionManager" />  
  27.         <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>  
  28.     </beans:bean>  
  29.   
  30.     <!-- 登录验证器Authentication -->  
  31.     <beans:bean id="loginFilter"  
  32.         class="com.epro.crm.util.security.MyUsernamePasswordAuthenticationFilter">  
  33.         <!-- 处理登录的action -->  
  34.         <beans:property name="filterProcessesUrl" value="/SecurityCheck" />  
  35.         <!-- 验证成功后的处理-->  
  36.         <beans:property name="authenticationSuccessHandler"  
  37.             ref="loginLogAuthenticationSuccessHandler" />  
  38.         <!-- 验证失败后的处理-->  
  39.         <beans:property name="authenticationFailureHandler"  
  40.             ref="simpleUrlAuthenticationFailureHandler" />  
  41.         <beans:property name="authenticationManager" ref="myAuthenticationManager" />  
  42.         <!-- 注入DAO为了查询相应的用户 -->  
  43.         <beans:property name="loginService" ref="loginService" />  
  44.         <beans:property name="logService" ref="logService" />  
  45.     </beans:bean>  
  46.   
  47.     <authentication-manager alias="myAuthenticationManager">  
  48.         <authentication-provider user-service-ref="myUserDetailsService">  
  49.             <password-encoder hash="md5" />  
  50.         </authentication-provider>  
  51.     </authentication-manager>  
  52.   
  53.     <beans:bean id="mySecurityMetadataSource"  
  54.         class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">  
  55.         <beans:constructor-arg  
  56.             type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />  
  57.         <beans:constructor-arg type="java.util.LinkedHashMap"  
  58.             ref="securityRequestMapFactoryBean" />  
  59.     </beans:bean>  
  60.   
  61.     <beans:bean id="antUrlPathMatcher"  
  62.         class="org.springframework.security.web.util.AntUrlPathMatcher" />  
  63.           
  64.     <beans:bean id="affirmativeBasedAccessDecisionManager"  
  65.         class="org.springframework.security.access.vote.AffirmativeBased">  
  66.         <beans:property name="decisionVoters" ref="roleDecisionVoter" />  
  67.     </beans:bean>  
  68.   
  69.     <beans:bean name="roleDecisionVoter"  
  70.         class="org.springframework.security.access.vote.RoleVoter" />  
  71.   
  72.     <beans:bean id="loginLogAuthenticationSuccessHandler"  
  73.         class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">  
  74.         <beans:property name="defaultTargetUrl" value="/page/main.jsp"></beans:property>  
  75.     </beans:bean>  
  76.   
  77.     <beans:bean id="simpleUrlAuthenticationFailureHandler"  
  78.         class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">  
  79.         <!-- 
  80.             可以配置相应的跳转方式。属性forwardToDestination为true采用forward false为sendRedirect 
  81.         -->  
  82.         <beans:property name="defaultFailureUrl" value="/page/login.jsp"></beans:property>  
  83.     </beans:bean>  
  84.   
  85.     <!-- 未登录的切入点 -->  
  86.     <beans:bean id="authenticationProcessingFilterEntryPoint"  
  87.         class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
  88.         <beans:property name="loginFormUrl" value="/page/login.jsp"></beans:property>  
  89.     </beans:bean>  
  90.   
  91. </beans:beans>  

------------------------------------------------------ 分割线 -----------------------------

后记:

由于权限配置信息,是由初始化mySecurityMetadataSource时,就由mySecurityMetadataSource读取提供的权限信息,并缓存与该类的私有成员变量中,所以重新加载时就需要重新新建一个对象

[java] view plaincopy
  1. public void loadSecurityInfos(){  
  2.         this.clear();  
  3.         List<Module> modules = moduleDao.getAll(new Module());  
  4.         Collections.sort(modules);  
  5.         for (Module module : modules) {  
  6.             RequestKey requestKey = new RequestKey(module.getPageUrl());  
  7.             Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();  
  8.             moduleDao.refresh(module);  
  9.             List<Role> roles = module.getRoles();  
  10.             if(roles != null){  
  11.                 for (final Role role : roles) {  
  12.                     configAttributes.add(new ConfigAttribute() {  
  13.                         public String getAttribute() {  
  14.                             return role.getCode();  
  15.                         }  
  16.                     });  
  17.                 }  
  18.             }  
  19.               
  20.             this.put(requestKey, configAttributes);  
  21.             log.info(module.getName()+ "模块 URL模式:" + requestKey + " 授权角色:"+ roles);  
  22.         }  
  23.     }