spring-mvc加spring security 的简单应用

先发几张图片看下大致的样子,美工不专业,有点渣,大家找自己想了解的就好。

spring-mvc加spring security 的简单应用_第1张图片

spring-mvc加spring security 的简单应用_第2张图片

spring-mvc加spring security 的简单应用_第3张图片

 

实现功能:

1、当没有登录的时候访问数据库有的资源,直接跳转到登录页面

2、用户登录后,在url栏直接访问没有权限的资源,跳转到403页面。

3、控制用户重复登录和次数

4、用户和角色都允许分配权限

 

先看下web.xml

<?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-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

spring-mvc加spring security 的简单应用_第4张图片

基础资源表baseResources

系统模块表application

spring-mvc加spring security 的简单应用_第5张图片

button表

spring-mvc加spring security 的简单应用_第6张图片

menu表

spring-mvc加spring security 的简单应用_第7张图片

用户角色表user_role

spring-mvc加spring security 的简单应用_第8张图片

用户表user

spring-mvc加spring security 的简单应用_第9张图片

角色表role

spring-mvc加spring security 的简单应用_第10张图片

由于application,button,menu表中没有定义一些其他的url资源,如:用户中心,获取菜单。。。而没有定义这些资源,当访问的时候就不会被拦截,还是可以直接访问。所以我定义了一个基础的资源表base_resources,当新建用户的时候就赋予用户这个权限,就管理了所有的url。

还有button,appliction,menu,base_resource表中的code是该资源需要的权限编码,我在privilege表也放入方便操作。

还有privilege表,主体可以为用户,角色。。。领域可以为模块,菜单,按钮,也就是who,what   how   某某在某某领域拥有什么。

大致就这样了

 

 

 

 

你可能感兴趣的:(SpringSecurity,权限管理,spring-mvc)