今天想把springsecurity集成到boot中,然后一个奇怪的错误就出现了,登陆地址老是出现403,搞了半天,找了各种问题,没想到只是需要关掉 csrf这个东西,如果你集成时出现登陆一直报403或者一直往登陆页面跳,那么你可能需要配置如下:
http.and().csrf().disable();
案例如下:
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/login.html", "/login", "/error")
.permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
出现的原因:
spring security默认开启了csrf防护,也就是他会在他的拦截器链上加上
org.springframework.security.web.csrf.CsrfFilter 这个类,这个类中的
doFilterInternal 方法如下:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//获取你当前请求(Session或Cookie)中的CsrfToken
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
//如果不存在就给你生成一个并保存到请求(Session或Cookie)中;
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
// 这里就是关键,这个匹配器,代码在如下:
//private final HashSet<String> allowedMethods = new HashSet<>(
// Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
//public boolean matches(HttpServletRequest request) {
// return !this.allowedMethods.contains(request.getMethod());
//}
// 根据上面的代码我们可以看出来,请求的方法类型只包括GET,HEAD,TRACE,OPTION
//而没有Post,但是你得登陆拦截地址是Post的,所以会不走下面的代码,继续往下走
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
// 他开始从请求头部取token了,如果没取到,那么他会去参数里面取
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
// 如果存到请求(Session或Cookie)中的csrfToken不是取出来的token,就会抛出对应的异常,
//如果你不开启 logging.level.org.springframework.security.web.csrf.CsrfFilter = debug
// 信息你都看不到,这个错误就会显得那么奇怪,
if (!csrfToken.getToken().equals(actualToken)) {
if (missingToken) {
this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));
}else {
this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
}
return;
}
filterChain.doFilter(request, response);
}
解决办法也不止关闭 csrf,你还可以自定义这个匹配类,然后配置进来!
/**
* 自定义跨站请求攻击匹配类
* @author qiaolin
* @version 2019/1/8
**/
@Component
public class MyRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
// 自定义一些访问规则
return false;
}
}
//下面代码为配置类中(简写了),我们需要把自己的匹配规则类配置
@Autowired
private RequestMatcher requestMatcher;
protected void configure(HttpSecurity http) throws Exception {
http.csrf().requireCsrfProtectionMatcher(requestMatcher);
}
如果你害怕 csrf 攻击,那么你可以看看spring secuirty的官方文档,
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf-include-csrf-token
文档上介绍了JSP怎么做,ajax的时候怎么做,但是看代码模式都是前后端不分离的!如果是前后端分离暂时还没想好怎么做!