Spring Security3.1实践

 

   说明下:  本篇博客时间久远,数据库已被我重装系统删除。本人懒蛋一个,不想建了,如果学习使用的童鞋根据表结构自行建立吧。

 

 

 

   本例子是我在spring MVC3.0.5的基础上进行修改的,用的Spring Security3.1.2。

 

          数据库:mysql,开发工具:myeclipse8.6,tomcat6.0。

 

1.收集资料

http://blog.csdn.net/k10509806/article/details/6369131

http://www.cnblogs.com/wenxiu/archive/2011/01/22/1942084.html

http://ootabc.iteye.com/blog/688213

http://wenku.baidu.com/view/abf23846336c1eb91a375d83.html

http://www.cnblogs.com/zhangliang0115/archive/2012/04/02/2429584.html

2.数据库建表

采用基于角色-资源-用户权限管理设计。

2.1.用户表    test_user

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

USERNAME

用户名

Varchar(30)

NO

 

 

PASSWORD

密码

varchar(36)

NO

 

 

STATUS

状态

tinyint

 

 

0开启、

1关闭

2.2.角色表    test_role

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

NAME

角色名

varchar(50)

 

 

 

2.3.资源表    test_resource

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

NAME

资源名称

varchar(50)

 

 

 

URL

地址

Varchar(50)

 

 

 

TYPE

类型

Tinyint

 

 

 

2.4.用户角色表   test_user_role

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

UID

用户ID

varchar(36)

 

 

 

RID

角色ID

varchar(36)

 

 

 

2.5.角色资源表   test_role_resource

字段名

标示符

类型

有无空值

主键

备注

ID

ID

varchar(36)

NO

Y

编号

RSID

资源ID

varchar(36)

 

 

 

RID

角色ID

varchar(36)

 

 

 

3.梳理资料,整理思路

3.1.Spring Security3.1的2种常见方式

Ø  用户信息和权限存储于数据库,而资源和权限的对应采用硬编码配置。

Ø  细分角色和权限,并将角色、用户、资源、权限均都存储于数据库中。并且自定义过滤器,代替原来的FilterSecurityInterceptor过滤器;并分别实现AccessDecisionManager、UserDetailsService和InvocationSecurityMetadataSourceService,并在配置文件中进行相应配置。

Ø发现两者不可结合使用,会有问题。

4.代码整理

接下来开始着手代码编写,不管是两种实现方式中的哪种方式,个人感觉都需要把加载用户信息放在一个类里面管理,直观方便,结构清晰,不要用在配置文件直接写sql语句。

4.1.资源和权限对应写在配置文件中

1、     web.xml配置

     a)     启动时加载Spring的jdbc和security配置文件。

     b)      配置spring的servlet过滤器,使其能够识别Spring Controller。

     c)     加载Spring Security过滤器链代理,它按照顺序执行spring的权限过滤器。

     d)     其他业务加载,比如:log4j,字符集编码过滤器,session超时等。

 

Xml代码   收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <web-app version="2.5"   
  3.     xmlns="http://java.sun.com/xml/ns/javaee"   
  4.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
  5.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">  
  7.     
  8.    <context-param>  
  9.         <param-name>webAppRootKey</param-name>  
  10.         <param-value>springMvc</param-value>  
  11.    </context-param>  
  12.      
  13.     <!-- Listener log4jConfigLocation -->  
  14.     <listener>  
  15.         <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>  
  16.     </listener>  
  17.     
  18.   <context-param>  
  19.     <param-name>contextConfigLocation</param-name>  
  20.     <param-value>  
  21.         classpath:/module/applicationContext-jdbc.xml,  
  22.         classpath:/module/applicationContext-security.xml  
  23.     </param-value>  
  24.   </context-param>  
  25.     
  26.   <!-- Log4j -->  
  27.   <context-param>  
  28.     <param-name>log4jConfigLocation</param-name>  
  29.     <param-value>classpath:/config/log4j.properties</param-value>  
  30.   </context-param>  
  31.     
  32.   <listener>  
  33.     <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  34.   </listener>  
  35.     
  36.   <!-- 权限过滤器链 -->  
  37.   <filter>  
  38.     <filter-name>springSecurityFilterChain</filter-name>  
  39.     <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
  40.   </filter>  
  41.     
  42.   <filter-mapping>  
  43.     <filter-name>springSecurityFilterChain</filter-name>  
  44.     <url-pattern>/*</url-pattern>  
  45.   </filter-mapping>  
  46.     
  47.   <servlet>  
  48.         <servlet-name>springmvc</servlet-name>  
  49.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  50.         <init-param>  
  51.             <param-name>contextConfigLocation</param-name>  
  52.             <param-value>classpath:/module/applicationContext-servlet.xml</param-value>  
  53.         </init-param>  
  54.         <load-on-startup>1</load-on-startup>  
  55.     </servlet>  
  56.     
  57.     <servlet-mapping>  
  58.         <servlet-name>springmvc</servlet-name>  
  59.         <url-pattern>/</url-pattern>  
  60.     </servlet-mapping>  
  61.     
  62.   <!-- Spring 刷新Introspector防止内存泄露 -->  
  63.     <listener>  
  64.         <listener-class>  
  65.             org.springframework.web.util.IntrospectorCleanupListener  
  66.         </listener-class>  
  67.     </listener>  
  68.       
  69.     <!--  获取Spring Security session的生命周期-->  
  70.     <listener>  
  71.         <listener-class>  
  72.             org.springframework.security.web.session.HttpSessionEventPublisher   
  73.         </listener-class>  
  74.     </listener>  
  75.   
  76.     <!-- session超时定义,单位为分钟 -->  
  77.     <session-config>  
  78.         <session-timeout>20</session-timeout>  
  79.     </session-config>  
  80.     
  81.     
  82.   <filter>  
  83.     <filter-name>encodingFilter</filter-name>  
  84.     <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  85.   </filter>  
  86.     
  87.   <filter-mapping>  
  88.     <filter-name>encodingFilter</filter-name>  
  89.     <url-pattern>/*</url-pattern>  
  90.   </filter-mapping>  
  91.     
  92.   <welcome-file-list>  
  93.     <welcome-file>index.jsp</welcome-file>  
  94.   </welcome-file-list>  
  95. </web-app>  

 

 

  2、  application-security.xml文件的配置。application-servlet.xml配置不懂的参考spring MVC3.0.5搭建全程。

 

Xml代码   收藏代码
  1. <?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"  
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  6.                     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">  
  7.    
  8.   <http pattern="/resources/**" security="none"></http>   
  9.   <http pattern="/user/login" security="none"></http>  
  10.     
  11.   <http auto-config="true"  
  12.         use-expressions="true"   
  13.         access-denied-page="/user/denied">  
  14.   <!--   
  15.     default-target-url       指定了从登录页面登录后进行跳转的页面  
  16.     always-use-default-target   true表示登录成功后强制跳转  
  17.     authentication-failure-url  表示验证失败后进入的页面  
  18.     login-processing-url       设置验证登录验证地址,如果不设置,默认是j_spring_security_check  
  19.     username-parameter,password-parameter     设置登录用户名和密码的请求name,默认:j_username,j_password  
  20.     default-target-url="/user/home"   
  21.    -->  
  22.     <form-login login-page="/user/login"  
  23.                 always-use-default-target="true"  
  24.                 authentication-failure-url="/user/login?error=1"  
  25.                 login-processing-url="/logincheck"  
  26.                 authentication-success-handler-ref="successHandler"/>  
  27.       
  28.     <intercept-url pattern="/user/myjsp" access="hasRole('ROLE_USER')"/>  
  29.     <intercept-url pattern="/user/admin" access="hasRole('ROLE_ADMIN')"/>  
  30.       
  31.     <logout logout-url="/logout" logout-success-url="/user/login"/>  
  32.     <!--   
  33.          error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号   
  34.          session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。  
  35.     -->  
  36.     <session-management invalid-session-url="/user/timedout" session-fixation-protection="none">  
  37.         <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>  
  38.     </session-management>  
  39.     <!-- <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/> -->  
  40.   </http>  
  41.     
  42.   <authentication-manager alias="authManager">  
  43.     <authentication-provider user-service-ref="userServiceDetail">  
  44.         <!--<jdbc-user-service data-source-ref="dataSource"   
  45.                             authorities-by-username-query=""  
  46.                             group-authorities-by-username-query=""/> -->  
  47.         <password-encoder hash="md5">  
  48.             <salt-source user-property="username"/>   <!-- 盐值  [添加这个属性后,加密密码明文为:"密码明文{盐值}"] -->  
  49.         </password-encoder>  
  50.           
  51.     </authentication-provider>  
  52.   </authentication-manager>  
  53.     
  54. </beans:beans>  

问题:

 

    我自己写了个User实现UserDetails,发现同一个账号可以同时登陆,也就是说concurrency-control没有起到作用,参考了一下资料后,重写一下User的hashcode,equals方法就行了。【后来发现的问题,附件自己添加】

 

    详细参考:http://flashing.iteye.com/blog/823666

 

 

Java代码   收藏代码
  1. @Override  
  2. public int hashCode() {  
  3.     return username.hashCode();  
  4. }  
  5.   
  6. @Override  
  7. public boolean equals(Object obj) {  
  8.     User user = (User)obj;  
  9.     return this.username.equals(user.getUsername());  
  10. }  

 

解析:

     a、use-expressions

如:hasRole(‘ROLE_ADMIN’或hasIpAddress(‘127.0.0.1’))等,看不懂的可以参考下面链接。

http://static.springsource.org/spring-security/site/docs/3.0.7.RELEASE/reference/el-access.html

http://hougbin.iteye.com/blog/1526980

http://kongcodecenter.iteye.com/blog/1320021

   b、<password-encoder  hash=”md5”>

其属性hash就是加密的方法是什么?常用的可能是md5和sha吧。

主要说下<salt-source user-property=’username’>盐值:不加这个属性,spring验证密码时,直接用MD5加密后的值,与我们自己写的实现了UserDetailsService接口的类中loadUsersByUsername(String username)方法返回的UserDetails中的密码进行比较;如果加了这个属性并且设置user-property=’username’[不知道能不能设置其他值,或许也可以设置password,没有尝试],加密前的明文就成为”密码明文{盐值}”,这里的盐值为用户名。

   c、remember-me的实现策略参考下面:

     http://static.springsource.org/spring-security/site/docs/3.0.x/reference/remember-me.html

     http://blog.csdn.net/small_love/article/details/6641316

     http://xyz20003.iteye.com/blog/223282

   d、UserDetailsService可以通过手工设置几个用户的权限:

Java代码   收藏代码
  1. <user-service id="userDetailsService">  
  2.     <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />  
  3.     <user name="bob" password="bobspassword" authorities="ROLE_USER" />  
  4.   </user-service>  

     或者通过属性文件读取;

    <user-service id="userDetailsService" properties="users.properties"/>

     属性 文件内容格式为: username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]

Java代码   收藏代码
  1. jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled  
  2. bob=bobspassword,ROLE_USER,enabled  

   

   e、UserDetailsService实现类

 

Java代码   收藏代码
  1. /** 
  2.  * @description  项目实现的用户查询服务,将用户信息查询出来(用于实现用户的认证) 
  3.  * @author aokunsang 
  4.  * @date 2012-8-15 
  5.  */  
  6. public class MyUserDetailServiceImpl implements UserDetailsService {  
  7.   
  8.     private UserService userService;  
  9.       
  10.     @Override  
  11.     public UserDetails loadUserByUsername(String username)  
  12.             throws UsernameNotFoundException {  
  13.           
  14.         System.out.println("---------MyUserDetailServiceImpl:loadUserByUsername------正在加载用户名和密码,用户名为:"+username);  
  15.           
  16.         User user = userService.loadUserByUserName(username);  
  17.         if(user==null){  
  18.             throw new UsernameNotFoundException("用户名没有找到!");  
  19.         }  
  20.           
  21.         boolean enabled = true;                //是否可用  
  22.         boolean accountNonExpired = true;        //是否过期  
  23.         boolean credentialsNonExpired = true;     
  24.         boolean accountNonLocked = true;    
  25.           
  26.         Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();  
  27.         //如果你使用资源和权限配置在xml文件中,如:<intercept-url pattern="/user/admin" access="hasRole('ROLE_ADMIN')"/>;  
  28.         //并且也不想用数据库存储,所有用户都具有相同的权限的话,你可以手动保存角色(如:预订网站)。  
  29.         //authorities.add(new SimpleGrantedAuthority("ROLE_USER"));  
  30.           
  31.         List<Role> roles = userService.findUserRolesByUsername(username);  
  32.         for(Role role : roles){  
  33.             GrantedAuthority ga = new SimpleGrantedAuthority(role.getName());  
  34.             authorities.add(ga);      
  35.         }  
  36.         return new org.springframework.security.core.userdetails.User(  
  37.                 user.getUserName(),  
  38.                 user.getPassWord(),   
  39.                 enabled,   
  40.                 accountNonExpired,   
  41.                 credentialsNonExpired,   
  42.                 accountNonLocked,   
  43.                 authorities);  
  44.     }  
  45.     /** 
  46.      * @param userService the userService to set 
  47.      */  
  48.     public void setUserService(UserService userService) {  
  49.         this.userService = userService;  
  50.     }  
  51.   
  52. }  

 

4.2.资源和配置文件存储在数据库中

需要自己手动写一个拦截器,提供查询数据库中的资源权限,提供验证用户是否具有访问URL地址的权限(3个类)。

        1、AbstractSecurityInterceptor继承类,同时实现Filter接口。

 

Java代码   收藏代码
  1. /** 
  2.  * @description 一个自定义的filter, 
  3.  *  必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 
  4.         我们的所有控制将在这三个类中实现 
  5.  * @author aokunsang 
  6.  * @date 2012-8-15 
  7.  */  
  8. public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor  
  9.         implements Filter {  
  10.   
  11.     private FilterInvocationSecurityMetadataSource fisMetadataSource;  
  12.       
  13.     /* (non-Javadoc) 
  14.      * @see org.springframework.security.access.intercept.AbstractSecurityInterceptor#getSecureObjectClass() 
  15.      */  
  16.     @Override  
  17.     public Class<?> getSecureObjectClass() {  
  18.         return FilterInvocation.class;  
  19.     }  
  20.   
  21.     @Override  
  22.     public SecurityMetadataSource obtainSecurityMetadataSource() {  
  23.         return fisMetadataSource;  
  24.     }  
  25.   
  26.     @Override  
  27.     public void destroy() {}  
  28.       
  29.     @Override  
  30.     public void doFilter(ServletRequest request, ServletResponse response,  
  31.             FilterChain chain) throws IOException, ServletException {  
  32.         //super.beforeInvocation(fi);源码    
  33.         //1.获取请求资源的权限    
  34.             //执行Collection<ConfigAttribute> attributes = SecurityMetadataSource.getAttributes(object);    
  35.         //2.是否拥有权限    
  36.             //this.accessDecisionManager.decide(authenticated, object, attributes);    
  37.         System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------开始拦截器了....");  
  38.         FilterInvocation fi = new FilterInvocation(request, response, chain);  
  39.         InterceptorStatusToken token = super.beforeInvocation(fi);  
  40.         try {  
  41.             fi.getChain().doFilter(fi.getRequest(), fi.getResponse());  
  42.         } catch (Exception e) {  
  43.             e.printStackTrace();  
  44.         }finally{  
  45.             super.afterInvocation(token,null);  
  46.         }  
  47.         System.out.println("------------MyFilterSecurityInterceptor.doFilter()-----------拦截器该方法结束了....");  
  48.     }  
  49.    
  50.     @Override  
  51.     public void init(FilterConfig config) throws ServletException {  
  52.           
  53.     }  
  54.       
  55.       
  56.     public void setFisMetadataSource(  
  57.             FilterInvocationSecurityMetadataSource fisMetadataSource) {  
  58.         this.fisMetadataSource = fisMetadataSource;  
  59.     }  
  60. }  

 

        2、FilterInvocationSecurityMetadataSource实现类

 

Java代码   收藏代码
  1. /** 
  2.  * @description  资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 
  3.  * @author aokunsang 
  4.  * @date 2012-8-15 
  5.  */  
  6. public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {  
  7.   
  8.     private UserService userService;  
  9.     /* 保存资源和权限的对应关系  key-资源url  value-权限 */  
  10.     private Map<String,Collection<ConfigAttribute>> resourceMap = null;   
  11.     private AntPathMatcher urlMatcher = new AntPathMatcher();  
  12.       
  13.     public MySecurityMetadataSource(UserService userService) {  
  14.         this.userService = userService;  
  15.         loadResourcesDefine();  
  16.     }  
  17.       
  18.     @Override  
  19.     public Collection<ConfigAttribute> getAllConfigAttributes() {  
  20.         return null;  
  21.     }  
  22.   
  23.     private void loadResourcesDefine(){  
  24.         resourceMap = new HashMap<String,Collection<ConfigAttribute>>();  
  25. //      Collection<ConfigAttribute> configAttributes1 = new ArrayList<ConfigAttribute>() ;  
  26. //      ConfigAttribute configAttribute1 = new SecurityConfig("ROLE_ADMIN");  
  27. //      configAttributes1.add(configAttribute1);  
  28. //      resourceMap.put("/leftmenu.action", configAttributes1);  
  29.           
  30.         System.out.println("MySecurityMetadataSource.loadResourcesDefine()--------------开始加载资源列表数据--------");  
  31.         List<Role> roles = userService.findAllRoles();  
  32.         for(Role role : roles){  
  33.             List<Resource> resources = userService.findResourcesByRoleName(role.getName());  
  34.             for(Resource resource : resources){  
  35.                 Collection<ConfigAttribute> configAttributes = null;  
  36.                 ConfigAttribute configAttribute = new SecurityConfig(role.getName());  
  37.                 if(resourceMap.containsKey(resource.getUrl())){  
  38.                     configAttributes = resourceMap.get(resource.getUrl());  
  39.                     configAttributes.add(configAttribute);  
  40.                 }else{  
  41.                     configAttributes = new ArrayList<ConfigAttribute>() ;  
  42.                     configAttributes.add(configAttribute);  
  43.                     resourceMap.put(resource.getUrl(), configAttributes);  
  44.                 }  
  45.             }  
  46.         }  
  47.     }  
  48.     /*  
  49.      * 根据请求的资源地址,获取它所拥有的权限 
  50.      */  
  51.     @Override  
  52.     public Collection<ConfigAttribute> getAttributes(Object obj)  
  53.             throws IllegalArgumentException {  
  54.         //获取请求的url地址  
  55.         String url = ((FilterInvocation)obj).getRequestUrl();  
  56.         System.out.println("MySecurityMetadataSource:getAttributes()---------------请求地址为:"+url);  
  57.         Iterator<String> it = resourceMap.keySet().iterator();  
  58.         while(it.hasNext()){  
  59.             String _url = it.next();  
  60.             if(_url.indexOf("?")!=-1){  
  61.                 _url = _url.substring(0, _url.indexOf("?"));  
  62.             }  
  63.             if(urlMatcher.match(_url,url))  
  64.                 return resourceMap.get(_url);  
  65.         }  
  66.         return null;  
  67.     }  
  68.   
  69.     @Override  
  70.     public boolean supports(Class<?> arg0) {  
  71.         System.out.println("MySecurityMetadataSource.supports()---------------------");  
  72.         return true;  
  73.     }  
  74.       
  75. }  

   

       3、AccessDecisionManager实现类

 

Java代码   收藏代码
  1. /** 
  2.  * @description  访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 ;做最终的访问控制决定 
  3.  * @author aokunsang 
  4.  * @date 2012-8-16 
  5.  */  
  6. public class MyAccessDescisionManager implements AccessDecisionManager {  
  7.   
  8.     /** 
  9.      * @description 认证用户是否具有权限访问该url地址 
  10.      *  
  11.      */  
  12.     @Override  
  13.     public void decide(Authentication authentication, Object obj,  
  14.             Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,InsufficientAuthenticationException {  
  15.         System.out.println("MyAccessDescisionManager.decide()------------------验证用户是否具有一定的权限--------");  
  16.         if(configAttributes==nullreturn;  
  17.         Iterator<ConfigAttribute> it = configAttributes.iterator();  
  18.         while(it.hasNext()){  
  19.             String needRole = it.next().getAttribute();  
  20.             //authentication.getAuthorities()  用户所有的权限  
  21.             for(GrantedAuthority ga:authentication.getAuthorities()){  
  22.                 if(needRole.equals(ga.getAuthority())){  
  23.                     return;  
  24.                 }  
  25.             }  
  26.         }  
  27.         throw new AccessDeniedException("--------MyAccessDescisionManager:decide-------权限认证失败!");  
  28.     }  
  29.   
  30.     /** 
  31.      * 启动时候被AbstractSecurityInterceptor调用,决定AccessDecisionManager是否可以执行传递ConfigAttribute。 
  32.      */  
  33.     @Override  
  34.     public boolean supports(ConfigAttribute configAttribute) {  
  35.         System.out.println("MyAccessDescisionManager.supports()------------角色名:"+configAttribute.getAttribute());  
  36.         return true;  
  37.     }  
  38.   
  39.     /** 
  40.      * 被安全拦截器实现调用,包含安全拦截器将显示的AccessDecisionManager支持安全对象的类型 
  41.      */  
  42.     @Override  
  43.     public boolean supports(Class<?> clazz) {  
  44.         System.out.println("MyAccessDescisionManager.supports()--------------------------------");  
  45.         return true;  
  46.     }  
  47.   
  48. }  

 

补充:还可以实现AuthenticationFailureHandler和AuthenticationSuccessHandler这两个接口,可以做一些验证失败和成功后的业务逻辑操作。(注意实现了这两个接口后,需要手动跳转路径),在<form-login>里面可配置。

        4、修改配置文件。

 

Xml代码   收藏代码
  1. <?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"  
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  6.                     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">  
  7.    
  8.   <http pattern="/resources/**" security="none"></http>   
  9.   <http pattern="/user/login" security="none"></http>  
  10.     
  11.   <http auto-config="true" access-denied-page="/user/denied">  
  12.   <!--   
  13.     default-target-url       指定了从登录页面登录后进行跳转的页面  
  14.     always-use-default-target   true表示登录成功后强制跳转  
  15.     authentication-failure-url  表示验证失败后进入的页面  
  16.     login-processing-url       设置验证登录验证地址,如果不设置,默认是j_spring_security_check  
  17.     username-parameter,password-parameter     设置登录用户名和密码的请求name,默认:j_username,j_password  
  18.     default-target-url="/user/home"   
  19.    -->  
  20.     <form-login login-page="/user/login"  
  21.                 always-use-default-target="true"  
  22.                 authentication-failure-url="/user/login?error=1"  
  23.                 login-processing-url="/logincheck"  
  24.                 authentication-success-handler-ref="successHandler"/>  
  25.       
  26.     <logout logout-url="/logout" logout-success-url="/user/login"/>  
  27.     <!--   
  28.          error-if-maximum-exceeded 后登陆的账号会挤掉第一次登陆的账号   
  29.          session-fixation-protection  防止伪造sessionid攻击,用户登录成功后会销毁用户当前的session。  
  30.     -->  
  31.     <session-management invalid-session-url="/user/timedout" session-fixation-protection="none">  
  32.         <concurrency-control max-sessions="1" error-if-maximum-exceeded="true"/>  
  33.     </session-management>  
  34.    <custom-filter ref="mySecurityFilter" before="FILTER_SECURITY_INTERCEPTOR"/>  
  35.   </http>  
  36.     
  37.   <authentication-manager alias="authManager">  
  38.     <authentication-provider user-service-ref="userServiceDetail">  
  39.         <!--<jdbc-user-service data-source-ref="dataSource"   
  40.                             authorities-by-username-query=""  
  41.                             group-authorities-by-username-query=""/> -->  
  42.         <password-encoder hash="md5">  
  43.             <salt-source user-property="username"/>   <!-- 盐值  [添加这个属性后,加密密码明文为:"密码明文{盐值}"] -->  
  44.         </password-encoder>  
  45.           
  46.     </authentication-provider>  
  47.   </authentication-manager>  
  48.     
  49.     
  50.   <!-- 登录失败后业务处理 -->  
  51.   <beans:bean id="failureHandler" class="com.aokunsang.security.LoginAuthenticationFailureHandler"></beans:bean>  
  52.   <!-- 登录成功业务处理 -->  
  53.   <beans:bean id="successHandler" class="com.aokunsang.security.LoginAuthenticationSuccesssHandler">  
  54.     <beans:property name="defaultUrl" value="/user/admin"></beans:property>  <!-- 可变换登录成功后的路径,验证用户是否拥有该权限 -->  
  55.   </beans:bean>  
  56.     
  57.   <!-- 自定义过滤器  -->  
  58.   <beans:bean id="mySecurityFilter" class="com.aokunsang.security.MyFilterSecurityInterceptor">  
  59.     <beans:property name="accessDecisionManager" ref="accessDescisionManager"></beans:property>  
  60.     <beans:property name="fisMetadataSource" ref="securityMetadataSource"></beans:property>  
  61.     <beans:property name="authenticationManager" ref="authManager"></beans:property>  
  62.   </beans:bean>  
  63.     
  64.   <beans:bean id="securityMetadataSource" class="com.aokunsang.security.MySecurityMetadataSource">  
  65.     <beans:constructor-arg name="userService" ref="userService"></beans:constructor-arg>  
  66.   </beans:bean>  
  67.     
  68.   <beans:bean id="accessDescisionManager" class="com.aokunsang.security.MyAccessDescisionManager"></beans:bean>  
  69.        
  70.   <beans:bean id="userServiceDetail" class="com.aokunsang.security.MyUserDetailServiceImpl">  
  71.     <beans:property name="userService">  
  72.         <beans:ref bean="userService"/>  
  73.     </beans:property>  
  74.   </beans:bean>  
  75. </beans:beans>  

 

4.3.替换form-login配置,实现自己的业务逻辑

如果想在登录之前做一些业务逻辑操作,比如:检查验证码的正确性(这个操作肯定要在验证用户名密码之前操作了)。那么我们自己继承UsernamePasswordAuthenticationFilter类,替换form-login里面的配置,完成检查验证码的操作;这里还需要注意一点,我们还需要实现一个未登录的切点(配置AuthenticationProcessingFilterEntryPoint或者LoginUrlAuthenticationEntryPoint),也就是没登录的都跳转到这个页面,相当于<form-login>中的login-page属性。

这里面的配置注意两点就行:

       1、在<http>中添加未登录切点配置entry-point-ref属性;

       2、去掉<form-login>,添加<custom-filter ref="XXXXFilter" position="FORM_LOGIN_FILTER"/>  

 

 说明:详细使用方法以及用户账户登录数控制<session-management><concurrency-control></session-management>可参考另一博客http://aokunsang.iteye.com/blog/1944111

 

http://blog.csdn.net/k10509806/article/details/6436987

http://hi.baidu.com/youxitou/item/de0fb00e76e15095a2df43cd

     

4.4.补充问题汇总

          在项目中使用spring Security3.1时,发现抛出的UsernameNotFoundException异常信息,总是打印出Bad credentials。如果我想得到比如:用户不存在,等信息,需要在xml中做设置。

 

Xml代码   收藏代码
  1. <!-- 使用该类主要解决例如UsernameNotFoundException抛出的异常全部显示Bad credentials[详细参考AbstractUserDetailsAuthenticationProvider:authenticate()];  
  2.              注意:如果通过用户名已经查询到用户信息(密码错误),此时抛出异常依然为Bad credentials[详细参考DaoAuthenticationProvider:additionalAuthenticationChecks()]  
  3.    -->  
  4.   <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">    
  5.     <beans:property name="userDetailsService" ref="userServiceDetail"></beans:property>   
  6.     <beans:property name="passwordEncoder" ref="md5Encoder"></beans:property>   
  7.     <beans:property name="hideUserNotFoundExceptions" value="false"/><!-- 【关键】没有这个将不能准确地报告异常(全部报告异常为:Bad credentials) -->    
  8.    </beans:bean>  
  9.     
  10.   <beans:bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"></beans:bean>  
  11.     
  12.   <authentication-manager alias="authManager">  
  13.     <authentication-provider ref="daoAuthenticationProvider"></authentication-provider>  
  14.   </authentication-manager>  
  15.     

 

 

5.附录

5.1.默认请求参数说明

默认值

说明

可设置

j_username

请求用户名

<from_login/>中

username_parameter

属性

j_password

请求密码

<from_login/>中

password_parameter

属性

j_spring_security_check

Post请求验证路径

<from_login/>中

login_processing_url属性

_spring_security_remember_me

“记住我”的请求name

暂无

sessionScope['SPRING_SECURITY_LAST_USERNAME']

 

Session中保存的最后一次登录的用户名

暂无

 

 

你可能感兴趣的:(spring,Security)