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代码
-
<?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"> <context-param> <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代码
-
<?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.1.xsd"> <http pattern="/resources/**" security="none"></http> <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代码
-
@Override public int hashCode() { return username.hashCode(); } @Override public boolean equals(Object obj) { User user = (User)obj; 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代码
- <user-service id="userDetailsService">
- <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
- <user name="bob" password="bobspassword" authorities="ROLE_USER" />
- </user-service>
或者通过属性文件读取;
<user-service id="userDetailsService" properties="users.properties"/>
属性 文件内容格式为: username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]
Java代码
- jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
- bob=bobspassword,ROLE_USER,enabled
e、UserDetailsService实现类
Java代码
-
/** * @description 项目实现的用户查询服务,将用户信息查询出来(用于实现用户的认证) * @author aokunsang * @date 2012-8-15 */ public class MyUserDetailServiceImpl implements UserDetailsService { private UserService userService; 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代码
-
/** * @description 一个自定义的filter, * 必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 我们的所有控制将在这三个类中实现 * @author aokunsang * @date 2012-8-15 */ public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor 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代码
-
/** * @description 资源源数据定义,将所有的资源和权限对应关系建立起来,即定义某一资源可以被哪些角色访问 * @author aokunsang * @date 2012-8-15 */ public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private UserService userService; /* 保存资源和权限的对应关系 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代码
-
/** * @description 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源 ;做最终的访问控制决定 * @author aokunsang * @date 2012-8-16 */ public class MyAccessDescisionManager implements AccessDecisionManager { /** * @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==null) return; 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代码
-
<?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.1.xsd"> <http pattern="/resources/**" security="none"></http> <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代码
-
<!-- 使用该类主要解决例如UsernameNotFoundException抛出的异常全部显示Bad credentials[详细参考AbstractUserDetailsAuthenticationProvider:authenticate()]; 注意:如果通过用户名已经查询到用户信息(密码错误),此时抛出异常依然为Bad credentials[详细参考DaoAuthenticationProvider:additionalAuthenticationChecks()] --> <beans:bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> <beans:property name="userDetailsService" ref="userServiceDetail"></beans:property> <beans:property name="passwordEncoder" ref="md5Encoder"></beans:property> <beans:property name="hideUserNotFoundExceptions" value="false"/><!-- 【关键】没有这个将不能准确地报告异常(全部报告异常为:Bad credentials) --> </beans:bean> 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中保存的最后一次登录的用户名 |
暂无 |