RBAC:基於角色的訪問控制(Role-Based Access Control)
先在web.xml 中配置一個過濾器(必須在Struts的過濾器之前)
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
然后就是編寫Spring安全的配置文件applicationContext-security.xml並配置到Spring解析的路徑下
Spring Security主要做兩件事,一件是認證,一件是授權。
認證
當用戶訪問受保護的信息卻沒有登錄獲得認證時,框架會自動將請求跳轉到登錄頁面
在http標簽中的
- <form-login login-page="/page/login.jsp" />
配置。且該登錄頁面必須是不被攔截的。故要配置上
- <intercept-url pattern="/page/login.jsp" filters="none" />
Web項目的認證如果在HTTP標簽中配置了auto-config="true",框架就會自動的配置多8?個攔截器。 默認表單登錄認證的是FORM_LOGIN_FILTER攔截器,我們可以直接寫自定義的UserDetailsService,在這個類中實現方法 UserDetails loadUserByUsername(String username),從數據庫獲取用戶信息,以及其擁有的角色。
- @Service("myUserDetailsService")
- public class MyUserDetailsServiceImpl extends BaseService implements UserDetailsService {
- @Resource
- private UserDao userDao;
- public UserDetails loadUserByUsername(String username)
- throws UsernameNotFoundException, DataAccessException {
- User user = userDao.getUserByUsername(username);
- List<Role> roles = user.getRoles();
- Collection<GrantedAuthority> authorities = new LinkedList<GrantedAuthority>();
- for (Role role : roles) {
- authorities.add(new GrantedAuthorityImpl(role.getCode()));
- }
- UserDetails userDetails = new org.springframework.security.core.userdetails.User(username,user.getPassword(),Constants.STATE_VALID.equals(user.getState()),true,true,true,authorities);
- return userDetails;
- }
- }
配置在
- <authentication-manager alias="myAuthenticationManager">
- <authentication-provider user-service-ref="myUserDetailsService">
- <password-encoder hash="md5" />
- </authentication-provider>
- </authentication-manager>
如果需要在登錄的時候,在HTTP SESSION中配置做些操作的。就得配置自定義的FORM_LOGIN_FILTER了 在HTTP標簽中加入
- <custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />
並配置
- <!-- 訪問控制驗證器Authority -->
- <beans:bean id="securityFilter"
- class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <beans:property name="accessDecisionManager"
- ref="affirmativeBasedAccessDecisionManager" />
- <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
- </beans:bean>
MyUsernamePasswordAuthenticationFilter 類是這么寫的
- public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
- public static final String USERNAME = "username";
- public static final String PASSWORD = "password";
- @Resource
- private LoginService loginService;
- private UserLoginFormBean userLoginFormBean = new UserLoginFormBean();
- @Resource
- private LogService logService;
- @Override
- public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
- String username = obtainUsername(request);
- String password = obtainPassword(request);
- HttpSession session = request.getSession();
- userLoginFormBean.setUsername(obtainUsername(request));
- userLoginFormBean.setPassword(obtainPassword(request));
- User user = loginService.login(userLoginFormBean);
- session.setAttribute(Constants.SESSION_USER, user);
- Log log = new Log(user,getIpAddr(request),"用戶登錄", null);
- logService.add(log);
- //UsernamePasswordAuthenticationToken實現 Authentication
- UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
- // Place the last username attempted into HttpSession for views
- // 允許子類設置詳細屬性
- setDetails(request, authRequest);
- // 運行UserDetailsService的loadUserByUsername 再次封裝Authentication
- return this.getAuthenticationManager().authenticate(authRequest);
- }
- }
getAuthenticationManager().authenticate(authRequest)是為了讓UserDetailService提供Detailed的信息並認證
授權
在授權時,系統默認通過FILTER_SECURITY_INTERCEPTOR認證。
如需自定義授權攔截器,我們在HTTP中在默認授權攔截器前配置了自定義的攔截器
- <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />
本平台采用基於請求URL地址的驗證方式
securityFilter的配置如下
- <!-- 訪問控制驗證器Authority -->
- <beans:bean id="securityFilter"
- class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <beans:property name="accessDecisionManager"
- ref="affirmativeBasedAccessDecisionManager" />
- <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
- </beans:bean>
采用默認的自定義的也是采用Spring默認的FilterSecurityInterceptor攔截器,accessDecisionManager也采用的是框架提供的affirmativeBasedAccessDecisionManager
采用投票者來判斷是否授權。
- <beans:bean id="affirmativeBasedAccessDecisionManager"
- class="org.springframework.security.access.vote.AffirmativeBased">
- <beans:property name="decisionVoters" ref="roleDecisionVoter" />
- </beans:bean>
- <beans:bean name="roleDecisionVoter"
- class="org.springframework.security.access.vote.RoleVoter" />
- <beans:bean id="mySecurityMetadataSource"
- class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">
- <beans:constructor-arg
- type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />
- <beans:constructor-arg type="java.util.LinkedHashMap"
- ref="securityRequestMapFactoryBean" />
- </beans:bean>
SecurityMetadataSource也是ss web框架提供的DefaultFilterInvocationSecurityMetadataSource,只是初始化參數中,一個選擇 antUrl匹配,還是正則匹配,另一個是提供自定義的通過securityRequestMapFactoryBean。在后者是一個 LinkedHashMap<RequestKey, Collection<ConfigAttribute>>類型,就是每一個URL匹配模式,所需要角色的集合。
- @Service("securityRequestMapFactoryBean")
- public class SecurityRequestMapFactoryBean extends
- LinkedHashMap<RequestKey, Collection<ConfigAttribute>> {
- @Resource
- private ModuleDao moduleDao;
- @PostConstruct
- public void loadSecurityInfos(){
- List<Module> modules = moduleDao.getAll(new Module());
- // List<Role> roles = roleDao.getAll(new Role());
- for (Module module : modules) {
- RequestKey requestKey = new RequestKey(module.getPageUrl());
- Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();
- for (final Role role : module.getRoles()) {
- configAttributes.add(new ConfigAttribute() {
- public String getAttribute() {
- return role.getCode();
- }
- });
- }
- this.put(requestKey, configAttributes);
- }
- }
- }
PS: 最終的件applicationContext-security.xml配置文件
- <pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?>
- <beans:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- 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.0.xsd">
- <http auto-config="true">
- <intercept-url pattern="/page/login.jsp" filters="none" />
- <intercept-url pattern="/LoginAction*" filters="none" />
- <intercept-url pattern="/common/**" filters="none" />
- <intercept-url pattern="/css/**" filters="none" />
- <intercept-url pattern="/common/**" filters="none" />
- <intercept-url pattern="/images/**" filters="none" />
- <intercept-url pattern="/js/**" filters="none" />
- <form-login login-page="/page/login.jsp" />
- <custom-filter ref="loginFilter" before="FORM_LOGIN_FILTER" />
- <custom-filter ref="securityFilter" before="FILTER_SECURITY_INTERCEPTOR" />
- </http>
- <!-- 訪問控制驗證器Authority -->
- <beans:bean id="securityFilter"
- class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <beans:property name="accessDecisionManager"
- ref="affirmativeBasedAccessDecisionManager" />
- <beans:property name="securityMetadataSource" ref="mySecurityMetadataSource"/>
- </beans:bean>
- <!-- 登錄驗證器Authentication -->
- <beans:bean id="loginFilter"
- class="com.epro.crm.util.security.MyUsernamePasswordAuthenticationFilter">
- <!-- 處理登錄的action -->
- <beans:property name="filterProcessesUrl" value="/SecurityCheck" />
- <!-- 驗證成功后的處理-->
- <beans:property name="authenticationSuccessHandler"
- ref="loginLogAuthenticationSuccessHandler" />
- <!-- 驗證失敗后的處理-->
- <beans:property name="authenticationFailureHandler"
- ref="simpleUrlAuthenticationFailureHandler" />
- <beans:property name="authenticationManager" ref="myAuthenticationManager" />
- <!-- 注入DAO為了查詢相應的用戶 -->
- <beans:property name="loginService" ref="loginService" />
- <beans:property name="logService" ref="logService" />
- </beans:bean>
- <authentication-manager alias="myAuthenticationManager">
- <authentication-provider user-service-ref="myUserDetailsService">
- <password-encoder hash="md5" />
- </authentication-provider>
- </authentication-manager>
- <beans:bean id="mySecurityMetadataSource"
- class="org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource">
- <beans:constructor-arg
- type="org.springframework.security.web.util.UrlMatcher" ref="antUrlPathMatcher" />
- <beans:constructor-arg type="java.util.LinkedHashMap"
- ref="securityRequestMapFactoryBean" />
- </beans:bean>
- <beans:bean id="antUrlPathMatcher"
- class="org.springframework.security.web.util.AntUrlPathMatcher" />
- <beans:bean id="affirmativeBasedAccessDecisionManager"
- class="org.springframework.security.access.vote.AffirmativeBased">
- <beans:property name="decisionVoters" ref="roleDecisionVoter" />
- </beans:bean>
- <beans:bean name="roleDecisionVoter"
- class="org.springframework.security.access.vote.RoleVoter" />
- <beans:bean id="loginLogAuthenticationSuccessHandler"
- class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
- <beans:property name="defaultTargetUrl" value="/page/main.jsp"></beans:property>
- </beans:bean>
- <beans:bean id="simpleUrlAuthenticationFailureHandler"
- class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
- <!--
- 可以配置相應的跳轉方式。屬性forwardToDestination為true采用forward false為sendRedirect
- -->
- <beans:property name="defaultFailureUrl" value="/page/login.jsp"></beans:property>
- </beans:bean>
- <!-- 未登錄的切入點 -->
- <beans:bean id="authenticationProcessingFilterEntryPoint"
- class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
- <beans:property name="loginFormUrl" value="/page/login.jsp"></beans:property>
- </beans:bean>
- </beans:beans></pre><br>
- ------------------------------------------------------ 分割線 -----------------------------------------------------------------<br>
- <br>
- 后記:
- <pre></pre>
- 由於權限配置信息,是由初始化mySecurityMetadataSource時,就由mySecurityMetadataSource讀取提供的權限信息,並緩存與該類的私有成員變量中,所以重新加載時就需要重新新建一個對象
- <pre></pre>
- <pre name="code" class="java">public void loadSecurityInfos(){
- this.clear();
- List<Module> modules = moduleDao.getAll(new Module());
- Collections.sort(modules);
- for (Module module : modules) {
- RequestKey requestKey = new RequestKey(module.getPageUrl());
- Collection<ConfigAttribute> configAttributes = new LinkedList<ConfigAttribute>();
- moduleDao.refresh(module);
- List<Role> roles = module.getRoles();
- if(roles != null){
- for (final Role role : roles) {
- configAttributes.add(new ConfigAttribute() {
- public String getAttribute() {
- return role.getCode();
- }
- });
- }
- }
- this.put(requestKey, configAttributes);
- log.info(module.getName()+ "模塊 URL模式:" + requestKey + " 授權角色:"+ roles);
- }
- }</pre><br>
- <br>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <pre></pre>
- <div style="padding-top:20px">
- <p style="font-size:12px;">版權聲明:本文為博主原創文章,未經博主允許不得轉載。</p>
- </div>
