spring-mvc加spring security 的簡單應用

先發幾張圖片看下大體的樣子,美工不專業,有點渣,你們找本身想了解的就好。html

 

實現功能:java

一、當沒有登陸的時候訪問數據庫有的資源,直接跳轉到登陸頁面web

二、用戶登陸後,在url欄直接訪問沒有權限的資源,跳轉到403頁面。面試

三、控制用戶重複登陸和次數spring

四、用戶和角色都容許分配權限數據庫

 

先看下web.xmlapache

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
 xmlns="http://java.sun.com/xml/ns/javaee" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
   
   <session-config>  
     <session-timeout>100</session-timeout>  
 </session-config>
 <listener>
    <listener-class>
   org.springframework.security.web.session.HttpSessionEventPublisher
  </listener-class>
 </listener>
 
 <!-- 編碼統一最好放最上面,最早加載,防止亂碼--> 
 <!-- Spring編碼轉換過濾器,將請求信息的編碼統一轉換爲UTF-8,避免中文亂碼 -->
 <filter>
  <filter-name>encodingFilter</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>
  <init-param>
   <param-name>forceEncoding</param-name>
   <param-value>true</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>encodingFilter</filter-name>
  <url-pattern>*.html</url-pattern>
 </filter-mapping>
 
 <!-- 而後接着是SpringSecurity必須的filter 優先配置,讓SpringSecurity先加載,防止SpringSecurity攔截失效-->   
    <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> 
    
    
 <!--配置文件加載   -->
    <context-param>
     <param-name>contextConfigLocation</param-name>
       <param-value>
        WEB-INF/classes/applicationContext.xml,
      WEB-INF/classes/config.xml,
      WEB-INF/spring-security.xml
       </param-value>
 </context-param>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
 
 <!--默認所對應的配置文件是WEB-INF下的{servlet-name}-servlet.xml,這裏即是:demoweb-servlet.xml--> 
 <servlet>
    <servlet-name>demoweb</servlet-name>
    <servlet-class>
     org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
  <load-on-startup>1</load-on-startup>    
   </servlet>
   <servlet-mapping>
    <servlet-name>demoweb</servlet-name>
    <url-pattern>*.html</url-pattern>
   </servlet-mapping>
 
 <welcome-file-list>
     <welcome-file>index.jsp</welcome-file>
   </welcome-file-list>
</web-app>

spring-mvc的配置文件demoweb-servlet.xml瀏覽器

<?xml version="1.0" encoding="UTF-8"?>
<beans 
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
  http://www.springframework.org/schema/context 
  http://www.springframework.org/schema/context/spring-context-3.2.xsd
  http://www.springframework.org/schema/mvc 
  http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
 
 <context:component-scan base-package="com.demo.web.app" />
 
 <!-- 修改@ResponseBody返回中文亂碼問題 -->
 <mvc:annotation-driven>
  <mvc:message-converters>     
         <bean class="org.springframework.http.converter.StringHttpMessageConverter">     
             <property name="supportedMediaTypes">     
                 <list>     
                     <value>text/plain;charset=UTF-8</value>
                     <value>text/html;charset=UTF-8</value>
                 </list>     
             </property>     
         </bean>      
     </mvc:message-converters>
 </mvc:annotation-driven>
 
 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
  p:order="2"
  p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
  
 <!-- FreeMarker配置 -->
 <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
  p:templateLoaderPath="/WEB-INF/ftl"
  p:defaultEncoding="UTF-8">
  <property name="freemarkerSettings">
   <props>
    <prop key="classic_compatible">true</prop>
   </props>
  </property>
 </bean>
 <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
  p:order="1"
  p:suffix=".ftl"
  p:contentType="text/html; charset=utf-8" />

  
 <!-- SpringMVC上傳文件時,須要配置MultipartResolver處理器 -->  
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">  
        <property name="defaultEncoding" value="UTF-8"/>  
        <!-- 指定所上傳文件的總大小不能超過200KB。注意maxUploadSize屬性的限制不是針對單個文件,而是全部文件的容量之和 -->  
        <property name="maxUploadSize" value="200000"/>  
    </bean>  
      
    <!-- SpringMVC在超出上傳文件限制時,會拋出org.springframework.web.multipart.MaxUploadSizeExceededException -->  
    <!-- 該異常是SpringMVC在檢查上傳的文件信息時拋出來的,並且此時尚未進入到Controller方法中 -->  
    <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> 
        <property name="exceptionMappings">  
            <props>  
                <!-- 遇到MaxUploadSizeExceededException異常時,自動跳轉到/WEB-INF/jsp/error_fileupload.jsp頁面 -->  
                <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">error_fileupload</prop>  
            </props>  
        </property>  
    </bean>
  
 <mvc:view-controller path="/"  view-name="/index" />
  
</beans>

以上這兩文件瞭解spring-mvc的應該都沒什麼問題spring-mvc

最重要的spring-security.xml文件緩存

<?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="false" entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/403.jsp"><!-- 當訪問被拒絕時,會轉到403.jsp -->
     <!--filters="none" 該路徑下不用過濾 -->
        <intercept-url pattern="/themes/**" filters="none" />
        <intercept-url pattern="/loginhtml.html" filters="none" />
        <!-- session沒用戶時訪問資源跳轉的默認指定登錄頁面,出錯後跳轉頁面,成功頁面 -->
<!--         <form-login login-page="/login.jsp" authentication-failure-url="/loginhtml.html?error=true"   default-target-url="/login/login.html" /> -->
        <!-- 退出銷燬session, 退出系統轉向url ,響應退出系統的url, 默認:/j_spring_security_logout-->  
        <logout invalidate-session="true" logout-success-url="/loginhtml.html"  logout-url="/j_spring_security_logout"/>
        <!-- 默認爲false,此值表示:用戶第二次登陸時,前一次的登陸信息都被清空 。       true系統拒絕登錄,後面試登錄次數 -->
        <session-management >
         <concurrency-control error-if-maximum-exceeded="true" max-sessions="1"/>
     </session-management>
        <http-basic />
  <!--position表示替換掉Spring Security原來默認的登錄驗證Filter。-->
  <custom-filter ref="loginFilter" position="FORM_LOGIN_FILTER"  />
        <!-- 增長一個filter,這點與Acegi是不同的,不能修改默認的filter了,這個filter位於FILTER_SECURITY_INTERCEPTOR以前 -->
        <custom-filter before="FILTER_SECURITY_INTERCEPTOR"
            ref="myFilter" />
    </http>
    
    <beans:bean id="loginFilter" class="com.demo.web.app.security.filter.MyUsernamePasswordAuthenticationFilter">
     <!-- 處理登陸的action -->
     <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>
     <!-- 驗證成功後的處理-->
     <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property> 
     <!-- 驗證失敗後的處理-->
     <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> 
     <beans:property name="authenticationManager" ref="authenticationManager"></beans:property> 
    </beans:bean>
    <!-- 未登陸的切入點 -->  
  <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  
   <beans:property name="loginFormUrl" value="/loginhtml.html"></beans:property>  
 </beans:bean>
   
 <beans:bean id="loginLogAuthenticationSuccessHandler"  class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">  
        <beans:property name="defaultTargetUrl" value="/member/index.html"></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="/loginhtml.html"></beans:property>
    </beans:bean>
 
    
    <!-- 一個自定義的filter,必須包含authenticationManager,accessDecisionManager,securityMetadataSource三個屬性,
    咱們的全部控制將在這三個類中實現,解釋詳見具體配置 -->
    <beans:bean id="myFilter" class="com.demo.web.app.security.filter.MySecurityFilter">
        <beans:property name="authenticationManager"  ref="authenticationManager" />
        <beans:property name="accessDecisionManager"  ref="myAccessDecisionManagerBean" />
        <beans:property name="securityMetadataSource"  ref="securityMetadataSource" />
    </beans:bean>
    
    <!-- 認證管理器,實現用戶認證的入口,主要實現UserDetailsService接口便可 -->
    <authentication-manager alias="authenticationManager">
        <authentication-provider
            user-service-ref="myUserDetailService">
        </authentication-provider>
    </authentication-manager>
    <beans:bean id="myUserDetailService" class="com.demo.web.app.security.MyUserDetailServiceImpl" />
    <!-- 訪問決策器,決定某個用戶具備的角色,是否有足夠的權限去訪問某個資源 -->
    <beans:bean id="myAccessDecisionManagerBean"
        class="com.demo.web.app.security.MyAccessDecisionManager">
    </beans:bean>
    
    <!-- 資源源數據定義,即定義某一資源能夠被哪些角色訪問 -->
    <beans:bean id="securityMetadataSource" class="com.demo.web.app.security.MySecurityMetadataSource" >
     <beans:constructor-arg name="resourcesService" ref="resourcesService"></beans:constructor-arg>
    </beans:bean>
    <beans:bean id="resourcesService" class="com.demo.web.app.service.resources.ResourcesService"></beans:bean>
   
</beans:beans>

下面把spring-secuity關鍵的說一下

<!-- 未登陸的切入點 -->  

  <beans:bean id="authenticationProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">  

   <beans:property name="loginFormUrl" value="/loginhtml.html"></beans:property>  

 </beans:bean>

也就是沒登陸的時候訪問資源跳轉到登陸頁,注意:要在http標籤上加

auto-config="false" entry-point-ref="authenticationProcessingFilterEntryPoint" access-denied-page="/403.jsp"

登陸:

<beans:bean id="loginFilter" class="com.demo.web.app.security.filter.MyUsernamePasswordAuthenticationFilter">

     <!-- 處理登陸的action -->

     <beans:property name="filterProcessesUrl" value="/j_spring_security_check"></beans:property>

     <!-- 驗證成功後的處理-->

     <beans:property name="authenticationSuccessHandler" ref="loginLogAuthenticationSuccessHandler"></beans:property> 

     <!-- 驗證失敗後的處理-->

     <beans:property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"></beans:property> 

     <beans:property name="authenticationManager" ref="authenticationManager"></beans:property> 

    </beans:bean>

login.jsp的action爲/j_spring_security_check,從新寫了一個MyUsernamePasswordAuthenticationFilter

package com.demo.web.app.security.filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import app.pojo.TUser;
import com.demo.web.app.service.user.UserService;
public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
 private Log log = LogFactory.getLog(MyUsernamePasswordAuthenticationFilter.class);
 public static final String VALIDATE_CODE = "validateCode";  
 public static final String USERNAME = "username";  
 public static final String PASSWORD = "password";
 @Autowired
 private UserService userService; 
 
 @Override
 public Authentication attemptAuthentication(HttpServletRequest request,
   HttpServletResponse response) throws AuthenticationException {
  log.info("登陸ing。。。。。。。。。。。");
  if (!request.getMethod().equals("POST")) {  
     throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());  
  }
  String username = obtainUsername(request);  
  String password = obtainPassword(request);
  TUser user = null;
  try {
   user = userService.getUserByUsername(username.trim());
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  if(user==null){
   throw new AuthenticationServiceException("用戶名錯誤!"); 
  }else{
   if(!user.getPassword().equals(password)){
    throw new AuthenticationServiceException("密碼錯誤!"); 
   }
  }
  //UsernamePasswordAuthenticationToken實現 Authentication
  UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
  // 容許子類設置詳細屬性 
  setDetails(request, authRequest);
  // 運行UserDetailsService的loadUserByUsername 再次封裝Authentication
  return super.attemptAuthentication(request, response);
 }
 
 
 @Override
 protected String obtainPassword(HttpServletRequest request) {
  Object obj = request.getParameter(PASSWORD);  
  return null == obj ? "" : obj.toString(); 
 }
 @Override
 protected String obtainUsername(HttpServletRequest request) {
  Object obj = request.getParameter(USERNAME);  
  return null == obj ? "" : obj.toString(); 
 }
}

 這裏主要的就是異常的拋出,能夠在login.jsp經過${SPRING_SECURITY_LAST_EXCEPTION.message}取得,具體就不談了。

權限控制的Filter,3個屬性

<beans:bean id="myFilter" class="com.demo.web.app.security.filter.MySecurityFilter">

        <beans:property name="authenticationManager"  ref="authenticationManager" />

        <beans:property name="accessDecisionManager"  ref="myAccessDecisionManagerBean" />

        <beans:property name="securityMetadataSource"  ref="securityMetadataSource" />

    </beans:bean>

 MySecurityFilter.java

package com.demo.web.app.security.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;

public class MySecurityFilter extends AbstractSecurityInterceptor implements Filter{
 private Log log = LogFactory.getLog(MySecurityFilter.class);
 //與applicationContext-security.xml裏的myFilter的屬性securityMetadataSource對應,   
 //其餘的兩個組件,已經在AbstractSecurityInterceptor定義   
 private FilterInvocationSecurityMetadataSource securityMetadataSource; 
 
 @Override
 public Class<? extends Object> getSecureObjectClass() {
  // TODO Auto-generated method stub
  //下面的MyAccessDecisionManager的supports方面必須放回true,不然會提醒類型錯誤   
  return FilterInvocation.class;
 }
 @Override
 public SecurityMetadataSource obtainSecurityMetadataSource() {
  // TODO Auto-generated method stub
  return this.securityMetadataSource;
 }

 public void destroy() {
  // TODO Auto-generated method stub
  
 }
 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {
  // TODO Auto-generated method stub
  FilterInvocation fi = new FilterInvocation(request, response, chain);  
  invoke(fi);
 }
 public void init(FilterConfig filterConfig) throws ServletException {
  // TODO Auto-generated method stub
 }
 private 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); 
  log.info("------------MyFilterSecurityInterceptor.doFilter()-----------開始攔截器了....");
  InterceptorStatusToken token = super.beforeInvocation(fi);  
  try { 
//   Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);
//   this.accessDecisionManager.decide(authenticated, object, attributes); 
      fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  } catch (Exception e) {
   e.printStackTrace();
  }finally {  
   super.afterInvocation(token, null);  
  }
  log.info("------------MyFilterSecurityInterceptor.doFilter()-----------攔截器該方法結束了....");
 }
 public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
  return securityMetadataSource;
 }
 public void setSecurityMetadataSource(
   FilterInvocationSecurityMetadataSource securityMetadataSource) {
  this.securityMetadataSource = securityMetadataSource;
 }

}

MyUserDetailServiceImpl.java

package com.demo.web.app.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import app.pojo.Application;
import app.pojo.Button;
import app.pojo.Menu;
import app.pojo.Privilege;
import app.pojo.TUser;
import com.demo.web.app.common.util.CommonUtil;
import com.demo.web.app.security.user.UserSession;
import com.demo.web.app.service.resources.ResourcesService;
import com.demo.web.app.service.user.UserService;
public class MyUserDetailServiceImpl implements UserDetailsService{
 
 private Log log = LogFactory.getLog(MyUserDetailServiceImpl.class);
 
 @Autowired
 private UserService userService;
 @Autowired
 private ResourcesService resourcesService;
 public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException {
  log.info("獲取用戶信息保存到全局緩存securityContextHolder,usernmae="+username);
  TUser user = null;
  user =userService.getUserByUsername(username);
  if(user==null){
   throw new UsernameNotFoundException(username);
  }
  Collection<GrantedAuthority> grantedAuths = obtionGrantedAuthorities(user);
  
  boolean enables = true;  
  boolean accountNonExpired = true;  
  boolean credentialsNonExpired = true;  
  boolean accountNonLocked = true;  
  UserSession userSession = new UserSession(user.getUsername(), user.getPassword(), enables, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuths);
  userSession.setId(user.getId());
  return userSession;
 }
 
 //取得用戶的權限   
 private List<GrantedAuthority> obtionGrantedAuthorities(TUser user) {
  List<GrantedAuthority> authSet = new ArrayList<GrantedAuthority>();
//  authSet.add(new GrantedAuthorityImpl("ROLE_USER"));  //用戶基本權限
  List<Privilege> ps = new ArrayList<Privilege>();
  List<Privilege> psU = getPrivilegeByMasterId("user", user.getId());
  ps.addAll(psU);
  //先加載用戶的權限,再去找相應角色的權限,都沒有返回null
  List<String> roleIds = getRoleIdsByUserId(user.getId());
  for(String roleId : roleIds) {  
   List<Privilege> psR = getPrivilegeByMasterId("role",roleId);
   ps.addAll(psR);
  } 
  packAuthority(ps, authSet);
  return authSet;  
 }

 /**
  * 加載用戶擁有資源
  * @param ps
  * @param authSet
  */
 public void packAuthority(List<Privilege> ps,List<GrantedAuthority> authSet){
  for(Privilege p : ps){
   authSet.add(new GrantedAuthorityImpl(p.getAccessCode()));
  }
  
 /* for(Privilege p : ps){
   if(p.getPrivilegeAccess().equals("application")){
    Application application = getApplication(p.getPrivilegeAccessValue());
    log.info("加載application");
    authSet.add(new GrantedAuthorityImpl(application.getApplicationCode()));     //application
    for(Privilege pmenu : ps){
     if(pmenu.getPrivilegeAccess().equals("menu")){
      Menu menu = getMenu(pmenu.getPrivilegeAccessValue());
      log.info("加載menu");
      if(menu.getApplicationId().equals(application.getApplicationId())){
       authSet.add(new GrantedAuthorityImpl(menu.getMenuCode()));        //menu
       for(Privilege pbtn : ps){
        if(pbtn.getPrivilegeAccess().equals("button")){
         Button btn = getButton(pbtn.getPrivilegeAccessValue());
         log.info("加載button");
         if(btn.getMenuId().equals(menu.getMenuId())){
          authSet.add(new GrantedAuthorityImpl(btn.getBtnCode()));  //button
         }
        }
       }
      }
     }
    }
   }
  } */
 }

 private Button getButton(String buttonId) {
  Button button = resourcesService.getButtonById(buttonId);
  return button;
 }
 private Menu getMenu(String menuId) {
  Menu menu = resourcesService.getMenuById(menuId);
  return menu;
 }
 private List<Privilege> getPrivilegeByMasterId(String master,String id) {
  List<Privilege> list = resourcesService.getPrivilegeByMasterId(master, id);
  return list;
 }
 private Application getApplication(String applicationId) {
  Application application = resourcesService.getApplicationById(applicationId);
  return application;
 }
 private List<String> getRoleIdsByUserId(String id) {
  List<String> roleIds = resourcesService.getRoleIdsByUserId(id);
  return roleIds;
 }
 @Override
 public boolean equals(Object obj) {
  System.out.println(obj);
  return super.equals(obj);
 }
 @Override
 public int hashCode() {
  System.out.println(MyUserDetailServiceImpl.this.hashCode());
  return super.hashCode();
 }
 
}

MySecurityMetadataSource.java

package com.demo.web.app.security;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import com.demo.web.app.service.resources.ResourcesService;
import app.pojo.Application;
import app.pojo.BaseResources;
import app.pojo.Button;
import app.pojo.Menu;

public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource{
 private ResourcesService resourcesService;
 private Log log = LogFactory.getLog(MySecurityMetadataSource.class);
 public MySecurityMetadataSource() {
 }
 //spring調用
 public MySecurityMetadataSource(ResourcesService resourcesService) {
  this.resourcesService = resourcesService;
  loadResourceDefine();
 }
  /* 保存資源和權限的對應關係  key-資源url  value-權限 */
 private static Map<String, Collection<ConfigAttribute>> resourceMap = null;
 
 public Collection<ConfigAttribute> getAllConfigAttributes() {
  // TODO Auto-generated method stub
  return null;
 }
 
 //返回所請求資源所須要的權限
 //返回null是不執行下面MyAccessDecisionManager中的decide方法   出現null的狀況:訪問的資源在數據庫中沒有定義
 public Collection<ConfigAttribute> getAttributes(Object object)
   throws IllegalArgumentException {
  String requestUrl = ((FilterInvocation) object).getRequestUrl(); 
  log.info("MySecurityMetadataSource:getAttributes()---------------請求地址爲:"+requestUrl);
  if(resourceMap==null){
   loadResourceDefine(); 
  }
  Iterator<String> it = resourceMap.keySet().iterator();   
  while(it.hasNext()){   
   String _url = it.next();   
   if(requestUrl.indexOf("?")!=-1){   
    requestUrl = requestUrl.substring(0, requestUrl.indexOf("?"));   
   }   
   if(_url.equals(requestUrl))
    return resourceMap.get(_url);
  }
  log.info("請求的資源:"+requestUrl+" 在數據庫中沒有定義!");
  return null;
 // return resourceMap.get(requestUrl);
 }
 //加載數據庫資源,封裝成Map<String, Collection<ConfigAttribute>>
 private void loadResourceDefine(){
  log.info("MySecurityMetadataSource.loadResourcesDefine()--------------開始加載資源列表數據--------");
  if(resourceMap == null) {  
   resourceMap = new HashMap<String, Collection<ConfigAttribute>>();
   List<BaseResources> baseResources = resourcesService.getBaseResources();
   List<Application> applications = resourcesService.getApplication();
   List<Menu> menus = resourcesService.getMenu();
   List<Button> buttons = resourcesService.getButton();
   for(BaseResources base : baseResources){
    Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();
    if(base.getBaseResourcesUrl()!=null && !base.getBaseResourcesUrl().equals("")){
     ConfigAttribute configAttribute = new SecurityConfig(base.getBaseResourcesCode());
     configAttributes.add(configAttribute);
     resourceMap.put(base.getBaseResourcesUrl(), configAttributes);
    }
   }
   for(Application application : applications){
    Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>(); 
    if(application.getApplicationUrl()!=null && !application.getApplicationUrl().equals("")){
     ConfigAttribute configAttribute = new SecurityConfig(application.getApplicationCode());
     configAttributes.add(configAttribute);
     resourceMap.put(application.getApplicationUrl(), configAttributes);
    }
   }
   for (Menu menu : menus) {
    Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();  
    //以menuCode封裝爲Spring的security Object   
    if(menu.getMenuUrl()!=null && !menu.getMenuUrl().equals("")){
     ConfigAttribute configAttribute = new SecurityConfig(menu.getMenuCode());  
     configAttributes.add(configAttribute);
     resourceMap.put(menu.getMenuUrl(), configAttributes);
    }
   }
   for(Button button : buttons){
    Collection<ConfigAttribute> configAttributes = new ArrayList<ConfigAttribute>();  
    if(button.getBtnUrl()!=null && !button.getBtnUrl().equals("")){
     ConfigAttribute configAttribute = new SecurityConfig(button.getBtnCode());  
     configAttributes.add(configAttribute);
     resourceMap.put(button.getBtnUrl(), configAttributes);
    }
   }
  }  
 }
 public boolean supports(Class<?> arg0) {
  // TODO Auto-generated method stub
//  System.out.println("MySecurityMetadataSource.supports()---------------------");
  return true;
 }

}

MyAccessDecisionManager.java

package com.demo.web.app.security;
import java.util.Collection;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
public class MyAccessDecisionManager implements AccessDecisionManager{
 
 private Log log = LogFactory.getLog(MyAccessDecisionManager.class);
 
 public void decide(Authentication authentication, Object object,
   Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,
   InsufficientAuthenticationException {
  log.info("MyAccessDescisionManager.decide()------------------驗證用戶是否具備必定的權限--------");
  if(configAttributes==null){
   return;
  }
  //所請求的資源擁有的權限(一個資源對多個權限)
  Iterator<ConfigAttribute> iterator = configAttributes.iterator(); 
  while(iterator.hasNext()) {  
   ConfigAttribute configAttribute = iterator.next();  
   //訪問所請求資源所須要的權限   
   String needPermission = configAttribute.getAttribute();
   log.info("請求資源所須要的權限~~~~~~~~~~~~~~~~~~needPermission is " + needPermission);
   //用戶所擁有的權限authentication   
   for(GrantedAuthority ga : authentication.getAuthorities()) {
    if(needPermission.equals(ga.getAuthority())) {
     return;
    }
   }
  }
  //沒有權限 
  throw new AccessDeniedException(" 沒有權限訪問! ");
 }
  /**  
  * 啓動時候被AbstractSecurityInterceptor調用,決定AccessDecisionManager是否能夠執行傳遞ConfigAttribute。  
  */ 
 public boolean supports(ConfigAttribute configAttribute) {
  // TODO Auto-generated method stub
  log.info("MyAccessDescisionManager.supports()------------角色名:"+configAttribute.getAttribute());
  return true;
 }
  /**  
 * 被安全攔截器實現調用,包含安全攔截器將顯示的AccessDecisionManager支持安全對象的類型  
 */  
 public boolean supports(Class<?> arg0) {
  // TODO Auto-generated method stub
//  System.out.println("MyAccessDescisionManager.supports()--------------------------------");
  return true;
 }
}

服務器啓動時先執行MySecurityMetadataSource的loadResourceDefine()加載數據庫資源,

登陸時會調用authenticationManager的authentication-provider,

MyUserDetailServiceImpl的loadUserByUsername把用戶信息和用戶權限加載進來放到全局變量securityContextHolder中

本人實驗這個好像也就在登陸的時候執行下,這個session過時的問題還不清楚,感受仍是首次登陸後計時,不是最後操做後計時。我也本身寫了userSession,繼承他的user

package com.demo.web.app.security.user;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import app.pojo.Application;
import app.pojo.Button;
import app.pojo.Menu;
/**
 * spring security中存的 用戶基本信息
 * 即session中存儲的用戶信息
 * @author Administrator
 *
 */
public class UserSession extends User{
 private String id;
 private List<Application> applications;
 private List<Menu> menus;
 private List<Button> buttons;
 
 public UserSession(String username, String password, boolean enabled,
   boolean accountNonExpired, boolean credentialsNonExpired,
   boolean accountNonLocked,
   Collection<? extends GrantedAuthority> authorities) {
  
  super(username, password, enabled, accountNonExpired, credentialsNonExpired,accountNonLocked, authorities);
 }
 
 public String getId() {
  return id;
 }
 public void setId(String id) {
  this.id = id;
 }
 public List<Application> getApplications() {
  return applications;
 }
 public void setApplications(List<Application> applications) {
  this.applications = applications;
 }
 public List<Menu> getMenus() {
  return menus;
 }
 public void setMenus(List<Menu> menus) {
  this.menus = menus;
 }
 public List<Button> getButtons() {
  return buttons;
 }

 public void setButtons(List<Button> buttons) {
  this.buttons = buttons;
 }
 @Override
 public boolean equals(Object rhs) {
  // TODO Auto-generated method stub
  return super.equals(rhs);
 }
 @Override
 public int hashCode() {
  // TODO Auto-generated method stub
  return super.hashCode();
 }
 
}

瀏覽器訪問時MySecurityFilter攔截執行InterceptorStatusToken token = super.beforeInvocation(fi); 

就會調用MySecurityMetadataSource 的getAttributes(Object object)方法,返回所請求資源所須要的權限,

再執行訪問決策器myAccessDecisionManagerBean中的decide方法。我是url相同既有權限訪問,基本上就這樣了。

再看下數據表的結構:

 權限關係表privilege

基礎資源表baseResources

系統模塊表application

button表

menu表

用戶角色表user_role

用戶表user

角色表role

因爲application,button,menu表中沒有定義一些其餘的url資源,如:用戶中心,獲取菜單。。。而沒有定義這些資源,當訪問的時候就不會被攔截,仍是能夠直接訪問。因此我定義了一個基礎的資源表base_resources,當新建用戶的時候就賦予用戶這個權限,就管理了全部的url。

還有button,appliction,menu,base_resource表中的code是該資源須要的權限編碼,我在privilege表也放入方便操做。

還有privilege表,主體能夠爲用戶,角色。。。領域能夠爲模塊,菜單,按鈕,也就是who,what   how   某某在某某領域擁有什麼。

大體就這樣了

相關文章
相關標籤/搜索