SpringSecurity(一)——认证实现

一、初步理解

SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。

当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。

核心过滤器:

  • (认证)UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求
  • ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedException和 AuthenticationException 
  • (授权)FilterSecurityInterceptor:负责权限校验的过滤器

二、Token(Jwt)登录校验流程

三、具体认证授权细节

下图是UsernamePasswordAuthenticationFilter处理用户名、密码,然后将用户名、密码、权限信息封装到Authentication对象中,再放到SecurityContextHolder中。

Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。

AuthenticationManager接口:定义了认证Authentication的方法

UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的 方法。

UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装 成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

认证

  1. 当用户登录时,前端将用户输入的用户名、密码信息传输到后台,后台用一个类对象将其封装起来,通常使用的是UsernamePasswordAuthenticationToken这个类。
  2. 程序负责验证这个类对象。验证方法是调用Service根据username从数据库中取用户信息到实体类的实例中,比较两者的密码,如果密码正确就成功登陆,同时把包含着用户的用户名、密码、所具有的权限等信息(用户id、昵称、是否管理员)的类对象放到SecurityContextHolder(安全上下文容器,类似Session)中去。
  3. 用户访问一个资源的时候,首先判断是否是受限资源。如果是的话还要判断当前是否未登录,没有的话就跳到登录页面。
  4. 如果用户已经登录,访问一个受限资源的时候,程序要根据url去数据库中取出该资源所对应的所有可以访问的角色,然后拿着当前用户的所有角色一一对比,判断用户是否可以访问(这里就是和权限相关)。

授权

  1. 在SpringSecurity中,会使用默认的FilterSecurityInterceptor来进行权限校验。在FilterSecurityInterceptor中会从SecurityContextHolder获取其中的Authentication,然后获取其中的权限信息。当前用户是否拥有访问当前资源所需的权限。
  2. 所以我们在项目中只需要把当前登录用户的权限信息也存入Authentication。然后设置我们的资源所需要的权限即可。

自定义登录认证接口:①调用ProviderManager的方法进行认证;②如果认证通过生成jwt;③把用户信息存入redis中

自定义权限信息查询:在UserDetailsService这个实现类中去查询数据库

四、自定义权限查询

修改UsernamePasswordAuthenticationFilter上图最右边的授权部分。

 1.自定义登陆接口

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/login")public R login(@RequestBody User user) {String jwt = userService.login(user);if (StringUtils.hasLength(jwt)) {return R.ok().message("登陆成功").data("token", jwt);}return R.error().message("登陆失败");}
}

 2.配置数据库校验登录用户

从之前的分析我们可以知道,我们可以自定义一个UserDetailsService,让SpringSecurity使用我们的 UserDetailsService。我们自己的UserDetailsService可以从数据库中查询用户名和密码。

创建一个类实现UserDetailsService接口,重写loadUserByUsername方法

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询用户信息QueryWrapper<User> queryWrapper=new QueryWrapper<>();queryWrapper.eq("user_name",username);User user = userMapper.selectOne(queryWrapper);//如果没有查询到用户,就抛出异常if(Objects.isNull(user)){throw  new RuntimeException("用户名或密码错误");}//TODO 查询用户对应的权限信息细节见SpringSecurity(二)——授权实现//如果有,把数据封装成UserDetails对象返回return new LoginUser(user);}
}

五、Jwt认证过滤器(自定义过滤器)

(1)在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要在 SecurityConfig中配置把AuthenticationManager注入容器

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig{/*** 登录时需要调用AuthenticationManager.authenticate执行一次校验*/@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();}
}

 (2)登录的业务逻辑层实现类

第一次登录,生成jwt存入redis

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overridepublic String login(User user) {//1.封装Authentication对象 ,密码校验,自动完成UsernamePasswordAuthenticationToken authentication =new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());//2.进行校验Authentication authenticate = authenticationManager.authenticate(authentication);//3.如果authenticate为空if (Objects.isNull(authenticate)) {throw new RuntimeException("登录失败"); //TODO 登录失败}//4.得到用户信息LoginUser loginUser = (LoginUser) authenticate.getPrincipal();//生成jwt,使用fastjson的方法,把对象转成字符串String loginUserString = JSON.toJSONString(loginUser);//调用JWT工具类,生成jwt令牌String jwt = JwtUtils.createJWT(loginUserString, null);//5.把生成的jwt存到redisString tokenKey = "token_" + jwt;stringRedisTemplate.opsForValue().set(tokenKey, jwt, JwtUtils.JWT_TTL / 1000);Map<String, Object> map = new HashMap<>();map.put("token", jwt);map.put("username", loginUser.getUsername());return jwt;}
}

(3)jwt认证校验过滤器

我们需要自定义一个过滤器,这个过滤器会去获取请求头中的token,对token进行解析取出其中的 userid。 使用userid去redis中获取对应的LoginUser对象。

然后封装Authentication对象存入SecurityContextHolder

/*** token验证过滤器   //每一个servlet请求,只会执行一次*/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate LoginFailureHandler loginFailureHandler;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,FilterChain filterChain)throws ServletException, IOException {try {//1.获取当前请求的url地址String url = request.getRequestURI();//如果当前请求不是登录请求,则需要进行token验证if (!url.equals("/user/login")) {//2.验证tokenthis.validateToken(request);}} catch (AuthenticationException e) {System.out.println(e);loginFailureHandler.onAuthenticationFailure(request, response, e);}//3.登录请求不需要验证tokendoFilter(request, response, filterChain);}/*** 验证token*/private void validateToken(HttpServletRequest request) throws AuthenticationException {//1.获取tokenString token = request.getHeader("Authorization");//如果请求头部没有获取到token,则从请求的参数中进行获取if (ObjectUtils.isEmpty(token)) {token = request.getParameter("Authorization");}if (ObjectUtils.isEmpty(token)) {throw new CustomerAuthenticationException("token不存在");}//2.redis进行校验String redisStr = stringRedisTemplate.opsForValue().get("token_" + token);if(ObjectUtils.isEmpty(redisStr)) {throw new CustomerAuthenticationException("token已过期");}//3.解析tokenClaims claims = null;try {claims = JwtUtils.parseJWT(token);} catch (Exception e) {throw new CustomerAuthenticationException("token解析失败");}//4.获取到用户信息String loginUserString = claims.getSubject();//把字符串转成loginUser对象LoginUser loginUser = JSON.parseObject(loginUserString, LoginUser.class);//创建身份验证对象UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());//5.设置到Spring Security上下文SecurityContextHolder.getContext().setAuthentication(authenticationToken);}
}

(4)把jwt过滤器注册到springsecurity过滤器链中

放在UsernamePasswordAuthenticationFilter前面

@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig{//自定义jwt校验过滤器@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {//配置关闭csrf机制http.csrf(csrf -> csrf.disable());//登陆失败处理器http.formLogin(configurer -> {configurer.failureHandler(loginFailureHandler);});http.sessionManagement(configurer ->// STATELESS(无状态): 表示应用程序是无状态的,不会创建会话。configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS));//请求拦截方式http.authorizeHttpRequests(auth -> auth.requestMatchers("/user/login").permitAll().anyRequest().authenticated());//!!!!!注册jwt过滤器!!!!!!!http.addFilterBefore(jwtAuthenticationTokenFilter,      UsernamePasswordAuthenticationFilter.class);//异常处理器http.exceptionHandling(configurer -> {configurer.accessDeniedHandler(customerAccessDeniedHandler);configurer.authenticationEntryPoint(anonymousAuthenticationHandler);});return http.build();   //允许跨域}/*** 登录时需要调用AuthenticationManager.authenticate执行一次校验*/@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55172.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python yolov8半自动标注

首先标注一部分图片&#xff0c;进行训练&#xff0c;生成模型&#xff0c;标注文件为xml方便后面统一做处理。 1、标注数据&#xff08;文件为xml, 转为txt用于训练&#xff0c;保留xml标签文件&#xff09; 2、模型训练&#xff08;训练配置、训练代码、&#xff09; 3、使用…

极狐GitLab 发布安全补丁版本 17.4.1、17.3.4、17.2.8

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

[已解决]DockerTarBuilder永久解决镜像docker拉取异常问题

前阵子发现阿里云的docker加速镜像失效了&#xff08;甚至连nginx都拉取不了&#xff09;&#xff0c;重新换了并且加多了网络上比较常用的dokcer加速源&#xff0c;可以解决一部分问题&#xff0c;但仍然有一些镜像的某个版本或一些比较冷的镜像就是拉取不了&#xff0c;原因未…

『网络游戏』数据库表格转储【25】

避免勿删数据库表格&#xff0c;可以将表格存储 放到桌面即可 现在将表格删除后点击 浏览桌面表格保存即可 修改客户端脚本&#xff1a;NetSvc.cs 目的是在数据库更新异常时弹出提示以便修改 本章结束

进程间通信、无名管道、有名管道

一、进程 1.1 进程间通信的概念 线程通信通过全局变量即可。 进程间通信是相互独立的&#xff0c;但是所有进程都共用一份内核空间&#xff0c;所以进程和进程之间的通信可以通过内核去进行。 1.2 进程间通信方式 共7种: 传统的进程间通信方式&#xff1a; 无名管道有名管道…

VSCode 查看 Git 的历史记录的三种技巧

前言 在我们日常开发工作过程中&#xff0c;可能经常会看到一些离谱的历史代码&#xff0c;或者当项目发生线上事故时&#xff0c;如何快速定位是谁提交的代码导致的&#xff1f; 作为前端开发者&#xff0c;VSCode 是目前最为流行的代码编辑工具&#xff0c;也是日常最常打开…

OPC UA与PostgreSQL如何实现无缝连接?

随着工业4.0的推进&#xff0c;数据交换和集成在智能制造中扮演着越来越重要的角色。OPC UA能够实现设备与设备、设备与系统之间的高效数据交换。而PostgreSQL则是一种强大的开源关系型数据库管理系统&#xff0c;广泛应用于数据存储和管理。如何将OPC UA与PostgreSQL结合起来&…

python pip安装requirements.txt依赖与国内镜像

python pip安装requirements.txt依赖与国内镜像 如果网络通畅&#xff0c;直接pip安装依赖&#xff1a; pip install -r requirements.txt 如果需要国内的镜像&#xff0c;可以考虑使用阿里的&#xff0c;在后面加上&#xff1a; -i http://mirrors.aliyun.com/pypi/simple --…

基于System.js的微前端实现(插件化)

目录​​​​​​​ 写在前面 一、微前端相关知识 &#xff08;一&#xff09;概念 &#xff08;二&#xff09; 优势 &#xff08;三&#xff09; 缺点 &#xff08;四&#xff09;应用场景 &#xff08;五&#xff09;现有框架 1. qiankun 2. single-spa 3. SystemJ…

GO之流程控制

一、流程控制简述 一&#xff09;流程控制的作用 流程控制语句是用来控制程序中语句执行顺序的语句&#xff0c;可以把语句组合成能完成一定功能的小逻辑块 二&#xff09;流程控制的分类 控制语句分为三类&#xff1a;顺序、选择和循环 顺序结构&#xff1a;依次执行&#xf…

通过Express + Vue3从零构建一个用户认证与授权系统(二)数据库与后端项目搭建与实现

前言 上一篇完成了系统的相关设计文档的编写&#xff0c;本文将详细介绍如何一步步使用 TypeScript 和 Express 搭建一个模块化、类型安全的用户认证与授权系统&#xff0c;包括数据库设计、后端项目搭建、用户认证、角色与权限管理、错误处理以及 Swagger 文档集成。 项目准…

Label Studio 半自动化标注

引言 Label Studio ML 后端是一个 SDK,用于包装您的机器学习代码并将其转换为 Web 服务器。Web 服务器可以连接到正在运行的 Label Studio 实例,以自动执行标记任务。我们提供了一个示例模型库,您可以在自己的工作流程中使用这些模型,也可以根据需要进行扩展和自定义。 1…

Ubuntu22.04环境下源码安装OpenCV 4.8.1

因为项目需要用OpenCV对yolov8模型进行推理&#xff0c;通过DNN模块&#xff0c;之前本地的OpenCV版本是4.5.4&#xff08;好像安装完ROS2 humble之后系统就自带了opencv&#xff09;&#xff0c;加载onnx模型一直报错&#xff0c;网上查询到需要4.7以上&#xff0c;干脆直接升…

开发教程 | 插件使用常见问题与调用攻略

Q1&#xff1a;插件是什么&#xff1f; 插件可以理解为是在一些专业领域上的单独的专精模型&#xff0c;比如专门生成PPT的模型、专门生成简历的模型。 大模型本身其实只是一个文字生成工具&#xff0c;只能根据自己在预训练过程中投入的语料以及用户的指令来回答问题。这给大…

内核定时器API实现点灯

1.内核定时器 定时器是一个很常用的功能&#xff0c;需要周期性处理的工作都要用到定时器。 Linux 内核定时器 采用系统时钟来实现&#xff0c;并不是6ull里面的硬件定时器。 Linux 内核定时器使用很简单&#xff0c;只需要提供超时时间(相当于定时值)和定时处理函数即…

500万人报名的软考到底是什么?有什么用?考什么?怎么报名?

软考是目前中国计算机领域最权威的认证考试之一&#xff0c;被广大IT从业者视为职业生涯发展的重要里程碑。通过参加软考&#xff0c;考生可以获得国家级资格认证&#xff0c;证明其具备一定的计算机专业知识和技能。本文将详细介绍软考的相关信息&#xff0c;帮助读者了解软考…

大数据-159 Apache Kylin 构建Cube 准备和测试数据

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

QT TCP服务器/客户端

服务器 首先要在.pro文件中添加network&#xff0c;否则将不能使用QTcpserver QT core gui network#ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QTcpSocket> #define PORT 8000QT_BEGIN_NAMESPACE namesp…

使用Rollup.js快速开始构建一个前端项目

Rollup 是一个用于 JavaScript 项目的模块打包器&#xff0c;它将小块代码编译成更大、更复杂的代码&#xff0c;例如库或应用程序。Rollup 对代码模块使用 ES6 模块标准&#xff0c;它支持 Tree-shaking&#xff08;摇树优化&#xff09;&#xff0c;可以剔除那些实际上没有被…

第7章 网络请求和状态管理

一、Axios 1 Axios概述 Axios是一个基于Promise的HTTP库&#xff0c;可以发送get、post等请求&#xff0c;它作用于浏览器和Node.js中。当运行在浏览器时&#xff0c;使用XMLHttpRequest接口发送请求&#xff1b;当运行在Node.js时&#xff0c;使用HTTP对象发送请求。 Axios的…