栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

SpringSecurity认证流程源码讲解

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringSecurity认证流程源码讲解

技术之路不进即退,无岸,又何谈回头。

从三个方面来说明

  1. 认证流程说明
  2. 认证结果如何在多个请求之间共享
  3. 获取认证用户信息
认证流程说明(SpringSecurity基本原理)

springSecurity其实就是一组过滤器,绿色的过滤器是由自己控制的,可以选择它是否出现。但是其他颜色就不可以了,并且他们的顺序也是死的。在springboot启动的时候会自动的进行加载。如下图所示:在这一组过滤器中,一个块就代表着一种方式,像表单和http Basic认证。橘色的框会根据之前绿色过滤器进行相应的判断,然后抛出异常。(例如身份认证没有通过) 然后由蓝色框会捕获抛出来的异常。

PS:一般来说链上的过滤器一般会有十几种

判断逻辑都是在 InterceptorStatusToken token = super.beforeInvocation(fi);(FilterSecurityInterceptor 124)执行的,然后 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); 这里实际是调用我们的API了,
举例

在这里我们就那上面那个实例来进行描述。在config中开启了任何url进行过滤之后。我们访问这样/mqtt/messagepub/new url会直接跳到橘色块,然后抛出未进行身份登录的异常,然后由蓝色框进行捕获,处理之后直接重定向到一个页面,进行登录。在登陆成功之后又会重新到橘色框,此次便不在抛异常了。

我们将依据下面这幅图来进行详细描述

1-认证处理流程

2-认证结果如何在多个请求之间共享

3-获取认证用户信息


在过滤器中依据用户名和密码构建了一个UsernamePasswordAuthenticationToken(Authentication的一个实现)对象,其实就是一个Authentication的实现,他封装了我们需要的认证信息。之后会调用AuthenticationManager.。这个类其实并不会去验证我们的信息,信息验证的逻辑都是在AuthenticationProvider里面,而Manager的作用则是去管理Provider,管理的方式是通过for循环去遍历(因为不同的登录逻辑是不一样的,比如表单登录、第三方登录。换句话说 不同的Provider支持的是不同的Authentication)。这里我们举一个实际的例子去看。
在 AuthenticationManager调用DaoAuthenticationProvider。而DaoAuthenticationProvider继承了AbstractUserDetailsAuthenticationProvider ,从而也就获得了其中的authenticate方法去进行验证。

  result = provider.authenticate(authentication);

AbstractUserDetailsAuthenticationProvider通过方法 retrieveUser拿到UserDetails这个类的信息(用户的信息),然后调用preAuthenticationChecks去进行预检查(里面有三个校验,没有密码过期的校验)。然后调用additionalAuthenticationChecks(附加检查),去进行密码的加密解密。
最后的postAuthenticationChecks区进行的密码是否过期的校验。
Details四个Blooen验证工过之后则会再次调用UsernamePasswordAuthenticationToken,当然这次调用与之前是不同的,之前的调用的是两个参数的,并没有获得用户权限的信息,因此调用父类方法的时候传的是空(super())。这次调用则传了相应的用户权限信息(三个参数,多了权限信息)。

public Authentication authenticate(Authentication authentication)
     throws AuthenticationException {
……
user = retrieveUser(username,
   (UsernamePasswordAuthenticationToken) authentication);
     }
     catch (UsernameNotFoundException notFound) {
  logger.debug("User '" + username + "' not found");

  if (hideUserNotFoundExceptions) {
      throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
  }
……
    try {
     preAuthenticationChecks.check(user);
     additionalAuthenticationChecks(user,
      (UsernamePasswordAuthenticationToken) authentication);
 }
 catch (AuthenticationException exception) {
     if (cacheWasUsed) {
  // There was a problem, so try again after checking
  // we're using latest data (i.e. not from the cache)
  cacheWasUsed = false;
  user = retrieveUser(username,
   (UsernamePasswordAuthenticationToken) authentication);
  preAuthenticationChecks.check(user);
  additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
     }
     else {
  throw exception;
     }
 }

 postAuthenticationChecks.check(user);

在这里我们对 认证流程说明 做一个简单的总结。
AuthenticationProvider去调用UserDetailsServicen拿到用户信息,然后做一些检查。最后把用户信息拼装到一个已经认证了的Authentication里面。然后Authentication则会沿着调用线最后返回到UernamePassword……Filter。

第二点

一说到共享,我感觉大家等会想到Session 那么问题来了。把什么放到session,有什么时候从session读取?

protected void successfulAuthentication(HttpServletRequest request,
     HttpServletResponse response, FilterChain chain, Authentication authResult)
     throws IOException, ServletException {

 if (logger.isDebugEnabled()) {
     logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
      + authResult);
 }

  //这里是重点
 SecurityContextHolder.getContext().setAuthentication(authResult);

 rememberMeServices.loginSuccess(request, response, authResult);

 // Fire event
 if (this.eventPublisher != null) {
     eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
      authResult, this.getClass()));
 }

 successHandler.onAuthenticationSuccess(request, response, authResult);

从上面我们可以了解 实际上这里是把我们认证成功的authtication放到securitycont里面,然后再放到SecourityContextHolder里面。

SecurityContextImpl implements SecurityContext,他重写了hashcode和equals保证
authentication的唯一性。而SecurityContextHolder是对threadLocal的一个封装。目的是为了方便SecurityContextPersistrncrFilter随时可以读取到。而这个过滤器在整个过滤器的最前面。因为在过滤器的最前端,所以请求最先进到这个过滤器,当完成相应之后最后经过它。所以他的作用

1---当请求进来的时候检查seesion,里面是否有SecurityContext,如果有就拿出来放到线程里,如果没有则直接放过。

2---当完成相应后,线程里有认证信息则放到Session里。

第三点
 @GetMapping("/me")
    public Object getCurrentUser(){
 return SecurityContextHolder.getContext().getAuthentication();
    }
//只获得UserDetails 信息
@GetMapping("/me1")
    public Object getCurrentUser(@AuthenticationPrincipal UserDetails user){
 return user;
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/235280.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号