当前位置: 首页 > news >正文

Security 权限控制的基本流程

Security的认证流程

先明白几个概念(在源码中找下面的方法)

1. AbstractAuthenticationToken抽象类, 令牌的基类

  • 其中的方法和属性
  1. authenticated:为true表示认证,为false表示未认证,用户有没有认证就是设置这个属性的值
  2. authorities:权限信息
  3. getPrincipal():返回认证主体(通常是用户信息)。getCredentials():返回认证凭证(例如密码、验证码等)。
实现AbstractAuthenticationToken抽象类的具体类
  • 其中的主要的方法和属性
  1. 构造方法:参数是用户名、密码的构造方法,方法中会设置authenticated未为认证状态也就是false
  2. 构造方法:参数是用户名、密码、权限的构造方法,方法中会设置authenticated未为认证状态也就是true
  3. static authenticated():调用构造方法,返回一个认证的对象

2. AuthenticationProvider接口,它的实现类就是认证者,其中定义了认证的逻辑

  • 其中主要的属性和方法
  1. authenticate():通过验证用户名和密码,调用AbstractAuthenticationToken类的static authenticated()方法,用户就通过认证
    • authenticate()方法中使用了UserDetailsService 的实现类,而这个实现类会调用它的loadUserByUsername方法,在这个方法中会去数据库查询用户的所有信息,并返回,继续在authenticate()中认证
  2. supports(): 这个认证者支持认证那一类AbstractAuthenticationToken的实现类

3. AuthenticationManager :Provider的管理者,所有认证者都由他管理

  • 作用:选择一个合适的Provider执行它里面的authenticate()
  • 它只有一个抽象方法:Authentication authenticate()
  • 如何选择合适的Provider

以下代码中的Token指的是AbstractAuthenticationToken的实现类,Provider指的是AuthenticationProvider的实现类,不然代码过长

  • 当执行authenticationManager.authenticate(Token)方法去认证Token时,authenticationManager会遍历每一个注册的Provider从它的supports()中找到可以认证这个TokenProvider

Security原生的令牌和认证,执行流程

  1. 如果使用Security提供的登录方式,制作令牌和调用认证都由UsernamePasswordAuthenticationFilter过滤器处理
  2. 用户输入用户名、密码点击确定后UsernamePasswordAuthenticationFilter过滤器获得用户名和密码,调用Authentication attemptAuthentication方法制造了一个UsernamePasswordAuthenticationToken令牌并交给认证者
  3. 调用DaoAuthenticationProvider中的authenticate方法,这个方法在它的父类AbstractUserDetailsAuthenticationProvider中,在这个方法调用了以下方法:
    • retrieveUser()抽象方法,由具体类DaoAuthenticationProvider实现,作用:返回从数据库查询的用户信息
    • additionalAuthenticationChecks()抽象方法,由DaoAuthenticationProvider实现,作用:用户输入的密码与数据库密码做比对
    • createSuccessAuthentication()抽象方法,由DaoAuthenticationProvider实现,作用:检查密码是否泄露,并调用父类认证成功的方法
    • createSuccessAuthentication()具体方法,作用:1. 将令牌UsernamePasswordAuthenticationToken设置为认证通过状态;2. 执行getAuthorities()方法设置用户拥有的权限,用于cotroller中鉴权

有来项目认证流程

  • 以下是使用用户名和密码登录的流程,因为没有使用使用默认的配置,原来接收用户名和密码的过滤器就无法获取到用户名和密码

  1. 用户发送用户名和密码,controller接收
// package com.youlai.boot.shared.auth.controller;
@RequestMapping("/api/v1/auth")
public class AuthController {@PostMapping("/login")public Result<AuthenticationToken> login( @RequestParam String username,@RequestParam String password) {AuthenticationToken authenticationToken = authService.login(username, password);return Result.success(authenticationToken);}
}
  1. controller调用service:AuthenticationToken authenticationToken = authService.login(username, password);
// package com.youlai.boot.shared.auth.service.impl;
@Service
public class AuthServiceImpl implements AuthService {@Overridepublic AuthenticationToken login(String username, String password) {// 1. 创建用于密码认证的令牌(未认证)UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(username.trim(), password);// 2. 执行认证(认证中)Authentication authentication = authenticationManager.authenticate(authenticationToken);// 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)AuthenticationToken authenticationTokenResponse =tokenManager.generateToken(authentication);SecurityContextHolder.getContext().setAuthentication(authentication);return authenticationTokenResponse;}
}
  1. new UsernamePasswordAuthenticationToken(username.trim(), password):生成令牌,其中主要的是用户输入的用户名、密码和认证成功与否的标志
  2. Authentication authentication = authenticationManager.authenticate(authenticationToken);
    • AuthenticationManager 是 Spring Security 提供的一个接口,负责选择一个合适的Provider去验证
      • 验证就是调用Providerauthenticate 方法。所以在执行authenticationManager.authenticate()时其实执行的是Provider.authenticate()
    • Provider.authenticate() 方法的作用:
      1. 获取到用户的数据库数据
      2. 用户输入的密码与数据库密码做比对
      3. 比对成功后将authenticationToken置为成功状态,并将数据库查询到的权限生成权限集合用于权限校验
  3. SysUserDetailsService.loadUserByUsername 方法
    • userService.getUserAuthInfo:从数据库获取用户详细信息(主要是密码、权限)
// package com.youlai.boot.core.security.service;
@Service
public class SysUserDetailsService implements UserDetailsService {private final UserService userService;// 根据用户名获取用户信息@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {try {UserAuthInfo userAuthInfo = userService.getUserAuthInfo(username);	// UserAuthInfo 数据表对应的实体类if (userAuthInfo == null) {throw new UsernameNotFoundException(username);}return new SysUserDetails(userAuthInfo);} catch (Exception e) {// 记录异常日志log.error("认证异常:{}", e.getMessage());// 抛出异常throw e;}}
}
  1. 实现了UserDetails接口的实体类
    • 用户的信息和权限就全有了
// package com.youlai.boot.core.security.model;
@Data
@NoArgsConstructor
public class SysUserDetails implements UserDetails {... // 省略了很多属性// 用户角色权限集合private Collection<SimpleGrantedAuthority> authorities;/*** 构造函数:根据用户认证信息初始化用户详情对象** @param user 用户认证信息对象 {@link UserAuthInfo}*/public SysUserDetails(UserAuthInfo user) {this.userId = user.getUserId();this.username = user.getUsername();this.password = user.getPassword();this.enabled = ObjectUtil.equal(user.getStatus(), 1);this.deptId = user.getDeptId();this.dataScope = user.getDataScope();// 初始化角色权限集合this.authorities = CollectionUtil.isNotEmpty(user.getRoles())? user.getRoles().stream()// 角色名加上前缀 "ROLE_",用于区分角色 (ROLE_ADMIN) 和权限 (user:add).map(role -> new SimpleGrantedAuthority(SecurityConstants.ROLE_PREFIX + role)).collect(Collectors.toSet()): Collections.emptySet();}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}
}
http://www.dtcms.com/a/137641.html

相关文章:

  • 基于 Python 的 ROS2 应用开发全解析
  • 【扩散模型(十三)】Break-A-Scene 可控生成,原理与代码详解(中)Cross Attn Loss 代码篇
  • tcp和udp的数据传输过程以及区别
  • neakyThrows 是 Lombok 库中的一个注解
  • LeetCode Hot100 刷题笔记(10)—— ACM格式输入输出练习
  • 2025年文件加密软件的作用及其在现代社会中的重要性
  • 类的加载过程
  • Policy Gradient思想、REINFORCE算法,以及贪吃蛇小游戏(一)
  • SDK游戏盾如何接入?复杂吗?
  • RCL谐振电压增益曲线
  • 【树形dp题解】dfs的巧妙应用
  • SpringCloud企业级常用框架整合--下篇
  • 在 Linux 中判断当前网络类型与网卡类型的实用方法(内外网判断 + 网卡分类)
  • Function Calling是什么?
  • springboot + vue3项目部署到服务器上面(宝塔Linux面板)
  • 充电宝项目中集成地图地址解析功能梳理
  • [特殊字符] 大模型微调实战:通过 LoRA 微调修改模型自我认知 [特殊字符]✨
  • L2-013 红色警报
  • 【专题刷题】双指针(二)
  • 带你从入门到精通——知识图谱(六. 知识融合)
  • 半导体设备通信标准—secsgem v0.3.0版本使用说明文档(2)之GEM(SEMI 30)
  • 零基础上手Python数据分析 (15):DataFrame 数据排序与排名 - 快速定位关键数据
  • 【leetcode hot 100 136】只出现一次的数字
  • openlayer的基本使用(区域绘制、点线绘制、手动绘制轨迹)
  • 【LaTeX】Misplaced alignment tab character . ^^I
  • 如何下载免费地图数据?
  • GKI 介绍
  • C++算法(9):数组作为函数参数,注意事项与实践
  • 【C++算法】61.字符串_最长公共前缀
  • 利用 Python 和 AI 技术创作独特的图像艺术作品