@Component
//自定义登陆成功与失败处理
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("自定义成功处理逻辑--------------------------");
//创建一个SecurityContext对象,并且设置上一步得到的Authentication
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession();
//将上一部得到的SecurityContext对象放入session中。到此,自定义用户信息的处理已经完成
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
System.out.println("--------------结束认证");
System.out.println("将用户信息存储在session中");
super.setDefaultTargetUrl("/index"); // 设置默认登陆成功的跳转地址
super.onAuthenticationSuccess(request, response, authentication);
// RequestCache requestCache = new HttpSessionRequestCache();
// DefaultSavedRequest savedRequest = (DefaultSavedRequest)requestCache.getRequest(request, response);
// if (savedRequest == null) {
// super.onAuthenticationSuccess(request, response, authentication);
// } else {
// String targetUrlParameter = this.getTargetUrlParameter();
// if (!this.isAlwaysUseDefaultTargetUrl() && (targetUrlParameter == null || !StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
// this.clearAuthenticationAttributes(request);
// String targetUrl = savedRequest.getRedirectUrl();
// this.logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
// this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
// } else {
// requestCache.removeRequest(request, response);
// super.onAuthenticationSuccess(request, response, authentication);
// }
// }
}
}
DefaultSavedRequest 封装了我们请求的所有信息。RedirectStrategy负责重定向我们的请求。我们可以根据自己的需求来改变注释掉的部分。
package com.springsecurity.handler;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
org.slf4j.Logger log = LoggerFactory.getLogger(this.getClass());
log.info("登录失败:" + exception.getMessage());
log.info("username=>" + request.getParameter("username"));
super.onAuthenticationFailure(request, response, exception);
// if (this.defaultFailureUrl == null) {
// this.logger.debug("No failure URL set, sending 401 Unauthorized error");
// response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
// } else {
// this.saveException(request, exception);
// if (this.forwardToDestination) {
// this.logger.debug("Forwarding to " + this.defaultFailureUrl);
// request.getRequestDispatcher(this.defaultFailureUrl).forward(request, response);
// } else {
// this.logger.debug("Redirecting to " + this.defaultFailureUrl);
// this.redirectStrategy.sendRedirect(request, response, this.defaultFailureUrl);
// }
// }
}
}
同样,根据实际请求,适当的改注释里面得一部分。完成自己的需要
可能有些人对这两段注释掉的不是很熟悉。其实很简单。说的直白一点
这里只是处理页面跳转的 相当于原生servlet里面我们处理页面跳转的那样
request.getRequestDispatcher(“s”).forward(request,response);
response.sendRedirect(“s”);
对于自己处理不了,或者不知道页面转发怎么处理,则调用super.xxx()交给相对应的父类去处理就好
package com.springsecurity.handler;
import org.springframework.security.core.AuthenticationException;
public class PassWordNotMathException extends AuthenticationException {
public PassWordNotMathException(String msg, Throwable t) {
super(msg, t);
}
public PassWordNotMathException(String msg) {
super(msg);
}
}
这个异常类必须是AuthenticationException 的子类!!!
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailService myUserDetailService;
@Autowired
private MyAuthenticationFailHandler myAuthenticationFailHandler;
@Qualifier("myAuthenticationProvider")
@Autowired
private AuthenticationProvider myAuthenticationProvider;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/login.html","/testlogin","/mylogin") // 不需要登录就可以访问
.permitAll()
.antMatchers("/user/**").hasAnyRole("user") // 需要具有ROLE_USER角色才能访问
.antMatchers("/admin/**").hasAnyRole("admin") // 需要具有ROLE_ADMIN角色才能访问
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/testlogin") // 设置登录页面
.loginProcessingUrl("/mylogin")
//.defaultSuccessUrl("/index") // 设置默认登录成功后跳转的页面
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailHandler)
.and()
.csrf().disable()// 禁用跨站攻击 关闭csrf 防止循环定向
;
}
// 密码加密方式
@Bean("bCryptPasswordEncoder")
public PasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
// 重写方法,自定义用户
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
//auth.userDetailsService(myUserDetailService);
}
@Bean("myAuthenticationProvider")
public AuthenticationProvider getMyAuthenticationProvider(){
MyAuthenticationProvider myAuthenticationProvider = new MyAuthenticationProvider();
myAuthenticationProvider.setUserDetailsService(myUserDetailService);
return myAuthenticationProvider;
}
}
大概总结下验证的流程
1 、UsernamePasswordAuthenticationFilter.doFilter()
1 方法里面调用attemptAuthentication()。在此方法内
封装UsernamePasswordAuthenticationToken对象然后调用 2
2、AuthenticationManager.authenticate()
此方法内,获取所有的Provider对象。然后调用其authenticate()方法
3、AuthenticationProvider.authenticate() 用其子类DaoAuthenticationProvider来具体说明。在此方法内,
首先获取UserDetail
接着验证UserDetail
其次密码比对
**上述任何一步发生异常,则验证不通过。**到第5步
成功走到这一步,说明登陆验证通过。接着第4步
4、SuccessHandler.onAuthenticationSuccess()调用成功处理器
5、若其中发生AuthenticationException 类异常,调用
FailureHandler.onAuthenticationFailure().否则,只抛出异常,而不进入异常处理器
为了大概弄清楚这个验证流程,从没看过源码的我可废了不少功夫。首先是不知道那些类是干什么的。还有那么多的接口,调用的时候是用的哪一个接口的实现类。其次是断点大概需要在什么位置。每一个重要的方法中不认识的类或者方法是干什么的 等等都得一一去了解。。。 累 ~ ~,