spring security 认证流程
1.认证流程
(1)用户发起登录请求后,首先进入 UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter)
AbstractAuthenticationProcessingFilter中doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// 1.判断请求是否是login和post,UsernamePasswordAuthenticationFilter的构造方法
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
//2.调用子类UsernamePasswordAuthenticationFilter的attemptAuthentication方法,
//在attemptAuthentication方法中创建了一个authenticated为false(即未授权)的UsernamePasswordAuthenticationToken,并传递给AuthenticationManager().authenticate()这个方法进行认证,
//认证成功后,返回一个authenticated=true的UsernamePasswordAuthenticationToken对象
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed
// authentication
return;
}
sessionStrategy.onAuthentication(authResult, request, response);
}
catch (InternalAuthenticationServiceException failed) {
logger.error(
"An internal error occurred while trying to authenticate the user.",
failed);
// 3.认证失败后通过调用AuthenticationFailureHandler 的onAuthenticationFailure 接口进行失败处理,可以通过继承AuthenticationFailureHandler 进行自定义失败逻辑
unsuccessfulAuthentication(request, response, failed);
return;
}
catch (AuthenticationException failed) {
// Authentication failed
// 4.认证失败后通过调用AuthenticationFailureHandler 的onAuthenticationFailure 接口进行失败处理,可以通过继承AuthenticationFailureHandler 进行自定义失败逻辑
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
// 5.认证成功后调用AuthenticationSuccessHandler 的onAuthenticationSuccess 接口进行成功处理,也可以通过继承AuthenticationSuccessHandler 自行实现成功处理逻辑
successfulAuthentication(request, response, chain, authResult);
}
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
// 根据用户名和密码生成 未认证的UsernamePasswordAuthenticationToken
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
setDetails(request, authRequest);
//将生成未认证的UsernamePasswordAuthenticationToken 交给AuthenticationManager 处理
return this.getAuthenticationManager().authenticate(authRequest);
}
AuthenticationManager 本身不包含认证逻辑,核心用来管理所有的AuthenticationProvider,通过交给合适的AuthenticationProvider来实现认证
(2)跳转到ProviderManager ,该类是AuthenticationManager 的实现类
不同的登录逻辑他的认证方式是不一样的,
springsecurity 支持多种认证逻辑,每一种认证逻辑的认证方式其实就是一种AuthenticationProvider。通过getProviders方法获取所有的AuthenticationProvider,通过supports来判断是否支持当前认证逻辑。
当选择好一个合适的AuthenticationProvider后,通过provider.authenticate()来让AuthenticationProvider 进行认证。
(3)传统表单登录的AuthenticationProvider主要是由AbstractUserDetailsAuthenticationProvider 来进行处理的,我们来看下它的authenticate()
首先调用retrieveUser来获取到数据库用户信息
当我们成功的读取UserDetails之后,下面开始对其进行认证
在上图中,我们可以看到认证校验分为 前校验、附加校验和后校验,如果任何一个校验出错,就会抛出相应的异常。所有校验都通过后,调用 createSuccessAuthentication() 返回认证信息。
在
createSuccessAuthentication方法中,重新new了一个UsernamePasswordAuthenticationToken,并将authorities传进去,设置authenticated为true
(4)至此认证信息被传递回UsernamePasswordAuthenticationFilter 中,在 UsernamePasswordAuthenticationFilter 的父类 AbstractAuthenticationProcessingFilter 的 doFilter() 中,会根据认证的成功或者失败调用相应的 handler:
二、多个请求共享session用户信息
spring security通过session保存用户认真信息
下面将 Spring Security 的认证流程补充完整,如下图:
在上一节认证成功的 successfulAuthentication()方法中,有一行语句:
SecurityContextHolder.getContext().setAuthentication(authResult);
三、获取用户信息
通过调用 SecurityContextHolder.getContext().getAuthentication() 就能够取得认证信息
@GetMapping("/me")
@ResponseBody
public Object me() {
return SecurityContextHolder.getContext().getAuthentication();
}
上面的写法有点啰嗦,我们可以简写成下面这种, Spring MVC 会自动帮我们从 Spring Security 中注入:
@GetMapping("/me")
@ResponseBody
public Object me(Authentication authentication) {
return authentication;
}
如果你仅想获取 UserDetails 对象,也是可以的,写法如下:
@GetMapping("/me")
@ResponseBody
public Object me(@AuthenticationPrincipal UserDetails userDetails) {
return userDetails;
}
转载https://jitwxs.blog.csdn.net/article/details/84703690
上一篇: JUC习题
推荐阅读
-
Spring Boot邮箱链接注册验证及注册流程
-
SpringBoot+Spring Security无法实现跨域的解决方案
-
详解Spring IOC 容器启动流程分析
-
Spring MVC的基本概念及处理的流程
-
详解Spring Boot 使用Spring security 集成CAS
-
详解Spring Security 简单配置
-
Java开发之spring security实现基于MongoDB的认证功能
-
SpringBoot+Spring Security+JWT实现RESTful Api权限控制的方法
-
Spring Security整合CAS的示例代码
-
spring security动态配置url权限的2种实现方法