转自:https://blog.csdn.net/weixin_37689658/article/details/92752890

1、基本配置使用

(1)创建配置类

创建一个配置类SecurityConfig继承自WebSecurityConfigurerAdapter,重写里面的configure(HttpSecurity http)这个方法,配置好需要认证的登录url,以及提交表单的url,这里除了登录url不需要认证之外,其他的url都需要认证才能访问,并且formLogin表名这是一个表单提交,loginprocessingUrl中是设置的提交登录表单的url。

@Override
protected void configure(HttpSecurity http) throws Exception {
.formLogin()
.loginPage("/authentication/require") //指定没有认证时跳转到的认证url
.loginProcessingUrl("/authentication/form") //提交登录表单的url
.and()
.authorizeRequests()
.antMatchers("/authentication/require").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}

(2)获取认证信息

我们需要继承一个UserDetailSevice接口并且加入到容器中,实现loadUserByUsername方法,里面的逻辑通常是从数据库查找出对应用户名的密码然后构造一个UserDetail对象,spring security会根据返回的这个带有正确用户信息的对象和前台传过来的用户名密码进行比对来判断是否认证通过。

/**
* 表单登录的时候会调用loadUserByUsername来验证前端传过来的账号密码是否正确
*/
@Component
public class MyUserDetailService implements UserDetailsService { private Logger logger = LoggerFactory.getLogger(MyUserDetailService.class); @Autowired
private PasswordEncoder passwordEncoder; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("登录用户名===========" + username);
//这里需要去数据库查询用户的账号密码来比对是否正确,以及账号是否过期等等
String password = passwordEncoder.encode("123456");
logger.info("数据库密码是==============" + password);
return new User(username, password,
true,
true,
true,
true,
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}

 

这样配置好的话,比如说我们访问一个一个http://localhost:8080/user的接口,那么spring security 发现配置中这个接口是需要拦截的,并且当前的请求还没有通过认证,就会重定向到loginPage设置的这个接口或者页面中去。当我们调用loginProcessingUrl这个接口去提交表单的时候,如果通过了认证,那么就会重定向到原来我们想要访问的接口中去了,如果认证不成功,那么就出现认证失败等信息。

那么对于这个过程,spring security内部的流程到底是怎样的尼?我们先通过一张图来看。

我们可以看到在spring security内部其实是通过一个过滤器链来实现认证流程的,比如说这里的UsernamePasswordAuthenticationFilter就是拦截我们通过表单提交接口提交的用户名和密码,如果是Basic提交的话,就会被BasicAuthenticationFilter拦截,最后的橙色FilterSecurityInterceptor是首先判断我们当前请求的url是否需要认证,如果需要认证,那么就看当前请求是否已经认证,是的话就放行到我们要访问的接口,否则重定向到认证页面。

2、认证流程

这里已表单提交为例,当我们提交表单时,UsernamePasswordAuthenticationFilter首先会拦截请求,而UsernamePasswordAuthenticationFilter是继承于AbstractAuthenticationProcessingFilter的,在这个抽象类中已经定义好了doFilter的方法,而里面有一个attemptAuthentication方法是由子类实现的。所以当提交表单时spring security会发现这个一个表单提交,然后就调用了UsernamePasswordAuthenticationFilter的doFilter方法

然后我们来看 UsernamePasswordAuthenticationFilter里面重写的这个方法

然后我们可以看到首先是创建了一个UsernamePasswordAuthenticationToken对象,把用户名和密码传进去,再调用了getAuthenticationManager().authentication()方法,并把 UsernamePasswordAuthenticationToken对象传进去,那么这个AuthenticationManager是什么尼,其实它是一个接口

我们这里要看的是它的实现类ProviderManager,进去找到它的authenticate方法

其实这个ProviderManager就是管理所有的Provider对象的,通过for循环遍历找到适合的provider对象来调用其authenticate方法,那么provider对象又是什么东西?其实它也是一个接口

可以看到它的实现类有很多,我们这里主要来看DaoAuthenticationProvider这个实现类,但是在这个类中并没有找到authenticate方法,那么我们来它的父类AbstractUserDetailAuthenticationProvider里面来看下

里面调用了一个子类实现的方法,我们看这个子类的这个方法l

里面拿到我们之前加入到容器中的userDetailService然后调用loadUserByUsername方法拿到我们包含数据库用户信息的UserDetail对象,并且捕获用户名找不到等异常,所以这个方法就是来获取我们之前定义的userDetailService返回的UserDetail。

回到其父类中去,父类拿到了UserDetail对象,对这个对象进行了一系列的判断

首先我们来看下前置判断

里面就是调用了UserDetail对象一些实现方法进行判断,比如说账户是否锁定,账户是否能用,账户是否过期的判断,判断通过后来到了密码判断

在判断了密码是否正确之后,后置判断就是来判断密码是否过期了,

前置判断判断账户异常问题,然后到密码判断,最后到密码是否过期判断,因为你登录先看你账户是否有异常才能,有异常的话就不进行密码判断了,密码正确也要看密码是够过期 ,所以这三个操作合乎常理。

判断都没问题之后,最后返回了一个authentication对象,这里把用户名,密码,权限都传到UsernamePasswordAuthenticationToken中去,因为所有判断都没问题,这些信息都是正确的了。

3.认证结果如何在多个线程中共享?

首先,我们还是先来看一张图

当请求过来的时候都会最先经过一个叫SecurityContextPersistenceFilter的过滤器,这个过滤器的作用就是在请求经过的时候检查session中是否有认证的authentication对象,如果有的话就把它放进一个叫securityContext ,然后再交给securityContextHolder处理

看一下securityContext

它是一个接口,由子类SecurityContextImpl实现,里面就是一个authentication属性,所以它的作用就是包装了authentication

接下来再看一下securityContextHolder

里面的getContext,clearContext方法都是使用的一个叫 SecurityContextHolderStrategy对象,点进去看一下这个对象

我们发现它又是一个接口,我们看它的实现类ThreadLocalSecurityContextHolderStrategy

里面只有一个ThreadLocal对象,放的是SecurityContext,我们知道ThreadLocal有线程隔离的作用,每一个对象在ThreadLocal中都是线程级别的。

接着我们回到securityContextpersistenceFilter中去看里面的doFilter方法

有一个loadContext方法是加载出来SecurityContext对象的,那么它是怎么加载的?点进去

它是一个接口,看它的实现类HttpSessionSecurityContextRepository

所以上面请求经过securityContextpersistenceFilter就是先从判断session是否有SecurityContext,有的话就放进当前线程中

那么响应的时候做什么?这里直接给出

那么认证成功后,是谁把认证对象放在放在当前请求中的?答案就是FilterSecurityInterceptor,FilterSecurityInterceptor会判断当前请求的url是否要拦截,要拦截的话如果当前用户没有经过认证,那么就跳转到认证页面,如果认证了就直接放行,并且把认证信息放在当前线程中。

总的流程:一个请求过来时通过各个过滤器,最后通过FilterSecurityInterceptor来判断这个请求url是否是不需要验证的,如果是
就直接访问到我们的接口api,如果不是的话,再判断当前请求线程中是否有authentication的认证对象,如果有就放行,如果没有就返回登录页面(比如这里我们设置的是登录表单的方式),来到登录页面输入账号密码登录后就会来到    UsernamePasswordAuthenticationFilter,经过一系列的操作,最后验证成功就会把认证对象authentication放进securityContext中,然后FilterSecurityInterceptor判断到当前请求线程中这个认证对象就放行,返回的时候最后会通过securityContextpersistenceFilter,判断当前线程是否有securityContext,如果有就放进session,那么下次再请求这个url的时候会首先通过securityContextpersistenceFilter这个过滤器,判断session中是否有securityContextduxiiang,如果有就放进当前请求线程中,然后最后经过FilterSecurityInterceptor时再判断当前请求线程是否有认证对象,由于最前面经过securityContextpersistenceFilter,已经从session中把认证对象放进了当前请求线程中,所以FilterSecurityInterceptor会直接放行,这样就访问到我们的接口api。

【权限管理】Spring Security 执行流程的更多相关文章

  1. 03 spring security执行流程分析

    spring security主要是依赖一系列的Filter来实现权限验证的,责任链设计模式是跑不了的.下面简单记录一下spring操作这些Filter的过程. 1. WebSecurityConfi ...

  2. 一文读懂Spring MVC执行流程

    说到Spring MVC执行流程,网上有很多这方面的文章介绍,但是都不太详细,作为一个初学者去读会有许多不理解的地方,今天这篇文章记录一下我学习Spring MVC的心得体会 话不多说,先上图:   ...

  3. Java——一文读懂Spring MVC执行流程

    说到Spring MVC执行流程,网上有很多这方面的文章介绍,但是都不太详细,作为一个初学者去读会有许多不理解的地方,今天这篇文章记录一下我学习Spring MVC的心得体会 话不多说,先上图: Sp ...

  4. spring boot:spring security用mysql实现动态权限管理(spring boot 2.3.3)

    一,动态权限管理的优点和缺点 1,优点: 因为控制权限的数据保存在了mysql或其他存储系统中, 可以动态修改权限控制,无需改动代码和重启应用,  权限变更时灵活方便 2,缺点: 权限的设置需要保存在 ...

  5. spring boot:spring security用mysql数据库实现RBAC权限管理(spring boot 2.3.1)

    一,用数据库实现权限管理要注意哪些环节? 1,需要生成spring security中user类的派生类,用来保存用户id和昵称等信息, 避免页面上显示用户昵称时需要查数据库 2,如果需要在页面上显示 ...

  6. 权限开发 spring security 3.0.7 序列1 数据库脚本

    spring  security  3 细粒度权限控制第一篇,关于权限的初始化测试数据库脚本. 空间脚本: drop user FrameworkTest cascade; drop tablespa ...

  7. 使用IDEA搭建一个Spring + AOP (权限管理 ) + Spring MVC + Mybatis的Web项目 (零配置文件)

    前言: 除了mybatis 不是零配置,有些还是有xml的配置文件在里面的. 注解是Spring的一个构建的一个重要手段,减少写配置文件,下面解释一下一些要用到的注解: @Configuration  ...

  8. Spring Security认证流程分析--练气后期

    写在前面 在前一篇文章中,我们介绍了如何配置spring security的自定义认证页面,以及前后端分离场景下如何获取spring security的CSRF Token.在这一篇文章中我们将来分析 ...

  9. spring security执行流程图

    今天看到非常多人转载了这篇文章,这里备注一下.原文来自CSDN我的博客. 近期在研究spring security的配置,研究了一个星期了,在官网看了下.仅仅配置出来了简单的登录,但不知如何从数据库读 ...

随机推荐

  1. 小刻也能看懂的Unraid系统使用手册:基础篇

    小刻也能看懂的Unraid系统使用手册 基础篇 Unraid系统简介 Unraid 的本体其实是 Linux,它主要安装在 NAS 和 All in One 服务器上,经常可以在 Linus 的视频里 ...

  2. datax的安装和使用(windows)

    github官方文档和项目:https://github.com/alibaba/DataX 下载后在windows环境下是可以直接用python编译执行的,但从github上下载的版本只支持pyth ...

  3. JavaScript学习之路(个人理解&经验):构造函数、类、实例

    构造函数: 声明:"构造函数"是一类函数的 "名字" 正确的读法是"构造函数"而不是"构造 / 函数" 构造函数形如普通 ...

  4. NOI Online 提高组 题解

    来补坑了-- 个人认为三道题难度差不多-- 还有要说一嘴,为啥我在其他网站代码都好好的,复制到 cnblogs 上 Tab 就成 8 空格了?不过也懒得改了. T1 序列 首先,遇到这种加一减一还带附 ...

  5. Java中lombok @Builder注解使用详解(十八)

    Lombok大家都知道,在使用POJO过程中,它给我们带来了很多便利,省下大量写get.set方法.构造器.equal.toString方法的时间.除此之外,通过@Builder注解,lombok还可 ...

  6. Netty入门(一):ByteBuf

    网络数据的基本单位总是字节.Java NIO 提供了 ByteBuffer 作为它的字节容器,但是这个类使用起来过于复杂,而且也有些繁琐.Netty 的 ByteBuffer 替代品是 ByteBuf ...

  7. LeetCode入门指南 之 链表

    83. 删除排序链表中的重复元素 存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 .返回同样按升序排列的结果链表. class Soluti ...

  8. 高效JAVA之用静态工厂方法代替构造器

    程序员这行干的久了,总会染上一些恶习,我就染上一个让人深恶痛绝,自己却津津乐道的习惯,还不想改的那种,它可以叫做强迫症,也可以叫做洁癖.那就是我不允许我的IDEA出现一点点警告,什么黄色背景,绿色波浪 ...

  9. Typora加七牛云实现实时图片自动上传

    Typora加七牛云实现实时图片自动上传 前言: ​ Typora是一款轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别.重点是免费! ​ 其风格简约 ...

  10. Java多线程 面试知识点总结1

    一.JMM(Java内存模型) 参考 老刘-JMM面试包过 HollisChuang-Java内存模型 <Java并发编程实战>Chapter-16 <深入理解Java虚拟机> ...