之前用spring security写一个登录demo的时候,一直遇到一个问题,在登录页面登录之后,一直不进行跳转,一直在登录界面,然后找了好久,才找到原因。
先来讲我遇到这个问题的解决办法。
先进行配置html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<input name="username" type="text"/>用户名
<br>
<input name="password" type="text" />密码
<br>
<!--在这里遇到了一个问题,因为登录的时候我最开始传的是${_csrf.getHeaderName()} ,
然后我看了CsrfFilter类的doFilterInternal方法后才发现${_csrf.parameterName}
才会有后面的验证,然后我把这里改了之后就成功了
但是我看源码里面其实也有获取getHeadName()这个方法,没理解到这里-->
<input th:name="${_csrf.parameterName}" th:value="${_csrf.token}">
<input type="submit"></input>
</form>
</body>
</html>
在csrfFilter里,每次登录都会有一个验证
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
boolean missingToken = (csrfToken == null);
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this.requireCsrfProtectionMatcher.matches(request)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Did not protect against CSRF since request did not match "
+ this.requireCsrfProtectionMatcher);
}
filterChain.doFilter(request, response);
return;
}
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
this.logger.debug(
LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)
: new MissingCsrfTokenException(actualToken);
this.accessDeniedHandler.handle(request, response, exception);
return;
}
filterChain.doFilter(request, response);
}
在String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
这里我不是很清楚为什么取不到request.getHeader(csrfToken.getHeaderName());
了解的大佬麻烦告诉下
然后后面就是
@Configuration //之前写过配置类的作用 这里就是相当于定义为一个Spring容器
@EnableWebSecurity //简单理解为默认开启crsf防护
public class MyConfig extends WebSecurityConfigurerAdapter {
Logger log = LoggerFactory.getLogger(MyConfig.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println(new BCryptPasswordEncoder().encode("111"));
log.info("{}值",new BCryptPasswordEncoder().encode("111"));
http.
authorizeRequests() .antMatchers("/hi","/success","/login.html").permitAll().
anyRequest().authenticated().
and().
//登录页面
formLogin().loginPage("views/login.html").permitAll().
//这里的登录确认页面,这里需要和html里面form表单里面的action一样
loginProcessingUrl("/login").permitAll().
//登录成功后固定跳转的页面
defaultSuccessUrl("/ok",true).permitAll().
//这里是失败后,可以获取失败的原因的地方
failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
e.printStackTrace();
}
}).permitAll().
and().
csrf() .csrfTokenRepository(new HttpSessionCsrfTokenRepository());
/*如果实在解决不了登录那个问题可以用
csrf().disable();
或者
csrf().ignoringAntMatchers("/login")
都可以登录成功
*/
}
//这里主要是对密码进行加密
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService().passwordEncoder();
/*我在这里也遇到一个问题,如果这里只写了user和password的话是会出错
然后我看了inMemoryAuthentication的源码之后,发现有6个还是7个必输项
但是如果只有2个参数的话,是会启动报错的,但是如果有3个参数就不会,
启动不会报错,感觉和官方的解释有点矛盾
*/
auth.inMemoryAuthentication().withUser("111").password(new BCryptPasswordEncoder().encode("111")).roles("admin").authorities("1");
}
/*
加密这里大致说一下 现在一般常见的就是md5,md5虽然很快但是很容易被破解
比如说字典啊,彩虹表之类的。所以一般情况下就需要加salt(加盐)。
加盐的大致理解可以为在原密码上加上固定盐或者随机盐
加固定盐的坏处显而易见,也会很容易被破解。
那么加随机盐的话,那么随机盐是怎么生成的,还有这个随机盐是保存在哪里,
随机盐的生成其实可以取一些逻辑,比如你的账户中,你的用户名中,然后把
生成的随机盐放放在自己的密码中,比如放在密码的最后面。
如果用户登录的话,从数据库取到有随机盐的密文密码,然后把密文密码进行分解
,把盐那到后在和输入的密码进行组合后进行加密后在对比
*/
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
如果本篇内容有问题,请第一时间联系我,我会第一时间修改。
谢谢大家。