把框架从Spring Security从3升级到4,结果出现了一大堆错误,每一个都是巨坑,几个非常熟悉的问题,花了我几乎一整天的时间,下面一一说来。
1. 验证始终无法通过
输入正确的用户名与密码,却始终收到这样的异常:
org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:151)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)把密码、密码加密器验证了好多遍,依旧存在这样的问题,后来追踪源码才发现,对于表单验证,Spring Security 4.2不支持参数更改,即用户名与密码必须为”username”与”password”,如果按照惯例,改为”j_username”与”j_password”,则验证无法通过。
<!-- 这两个参数配置现在无效 --><form-loginpassword-parameter="j_password"username-parameter="j_username"/>2. session was not found
在提交表单验证账户合法性时,又出现如下错误:
HTTP Status403 - Couldnot verifythe provided CSRFtoken because your session wasnot found.
type Status report
message Couldnot verifythe provided CSRFtoken because your session wasnot found.
description Accesstothe specified resource has been forbidden.错误很明显,是由于CSRF token引起的Session错误,解决办法很简单,禁止CSRF验证即可,如下:
<http><intercept-urlpattern="/login/*"access="IS_AUTHENTICATED_ANONYMOUSLY"/><intercept-urlpattern="/app/**"access="hasRole('USER')" /><!-- 必须添加此段声明,禁用CSRF功能 --><csrfdisabled="false"/><form-loginauthentication-success-forward-url="/app/home"authentication-failure-forward-url="/app/login/fail"always-use-default-target="false"default-target-url="/app/home"login-page="/index.html"login-processing-url="/login/check"/><logoutinvalidate-session="true"logout-url="/login/out"/></http>3. 无法找到验证地址
按照以往的配置,现在竟然找不到验证地址,提交验证表单时,j_spring_check竟然报404错误,配置如下:
<form-loginlogin-page="/sti/login/index"login-processing-url="/app/j_spring_check"password-parameter="j_password"username-parameter="j_username"authentication-failure-forward-url="/app/login/fail"authentication-success-forward-url="/sti/home"default-target-url="/sti/home"always-use-default-target="false"/><!-- Spring Security filter的配置如下 --><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>/app/*</url-pattern></filter-mapping><!-- Spring MVC过滤器的配置如下 --><servlet><servlet-name>stixuDispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>
META-INF/application-mvc-context.xml</param-value></init-param><load-on-startup>10</load-on-startup></servlet><servlet-mapping><servlet-name>stixuDispatcher</servlet-name><url-pattern>/app/*</url-pattern></servlet-mapping>仔细诊断原因,/app/j_spring_check竟然被转发到Spring MVC进行处理,Servlet的优先级竟然比Filter还高,这完全不可能啊!
换成其他地址/sti/j_spring_check,直接报404错误,这很好理解,因为过滤器只拦截/app/*相关的请求,所以最后的解决办法如下:
- 首先修改web.xml文件
<!-- web.xml文件 --><filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class></filter><!-- 改为拦截所有请求 --><filter-mapping><filter-name>springSecurityFilterChain</filter-name><url-pattern>/*</url-pattern></filter-mapping>- 修改Spring Security配置文件,将地址映射到根路径,如下:
<form-loginauthentication-success-forward-url="/app/login/success"authentication-failure-forward-url="/app/login/fail"always-use-default-target="false"default-target-url="/app/login/success"login-page="/app/login/index"
<!-- 修改到根路径,避免映射到SpringMVC--> login-processing-url="/login/check"/>4. 总结
Spring Security的安全解决方案非常完善,升级到4.3版本后,功能更加强大了,配置更加简单与方便了,但与Spring Security 3的差异较大,应用时需要特别注意。