Spring Security安全登录的调用过程以及获取权限的调用过程

1.第一次登录时候调用/user/login整个流程分析

post发送请求

(0)权限授理

首先调用SecurityConfig.java中的config函数将jwtAuthenticationTokenFilter过滤器放在UsernamePasswordAuthenticationFilter之前

@Override
protected void configure(HttpSecurity http) throws Exception{......    http.addFilterBefore(jwtAuthenticationTokenFilter,UsernamePasswordAuthenticationFilter.class);
}

(1)首先运行JwtAuthenticationTokenFilter extends OncePerRequestFilter中的doFilterInternal函数

    @Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {//1.获取tokenString token = httpServletRequest.getHeader("token");if(!StringUtils.hasText(token)){filterChain.doFilter(httpServletRequest,httpServletResponse);//这里可以放行的原因在于后面还有FilterSecurityInterceptor等其他过滤器,//如果没有认证后面还会被拦截下来return;//这里如果没有return,响应回来之后还会调用后面的代码}

这里由于没有获取到token,因此调用!StringUtils.hasText(token),这里使用

filterChain.doFilter(httpServletRequest,httpServletResponse);

继续运行其他拦截器,然后返回

(2)接下来运行LoginServiceImpl.java中的login函数

public class LoginServiceImpl implements LoginService{......public ResponseResult login(@RequestBody User user){UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());Authentication authentication = authenticationManager.authenticate(authenticationToken);   ...... } 
}

这里先根据UsernamePasswordAuthenticationToken放入user的name和password创建authenticationToken类

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());

接下来调用authenticate函数认证,这里authenticate函数会调用UserDetailsService的实现类UserDetailsServiceImpl函数中的loadUserByUsername进行读取数据库

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//这里的UserDetails是一个接口,因此需要写一个对应的实现类//查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(User::getUserName,username);User user = userMapper.selectOne(queryWrapper);//如果没有查询到用户,就抛出异常if(Objects.isNull(user)){throw new RuntimeException("用户名或者密码错误!");}//因为在UsernamePasswordAuthenticationFilter之后//会调用一个ExceptionTranslationFilter,所以只要出现//异常都会被捕获到//TODO 查询对应的权限信息List<String> list = new ArrayList<>(Arrays.asList("test","admin"));//封装成UserDetails返回,标注类AllArgsConstructor,因此可以直接传入return new LoginUser(user,list);}

(3)UserDetails实现类LoginUser的构造

这里创造新的类LoginUser并且传入user和list两大参数,进入LoginUser的构造函数和授权函数进行查看

public class LoginUser implements UserDetails {private User user;private List<String> permissions;public LoginUser(User user,List<String> permissions){this.user = user;this.permissions = permissions;}//redis默认不会将List<SimpleGrantedAuthority> authorities序列化,//因为会报异常,所以我们不需要把authorities这个集合存入到redis之中@JSONField(serialize = false)private List<SimpleGrantedAuthority> authorities;@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {if(authorities != null){return authorities;}authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());//SimpleGrantedAuthority::new使用流的构造器,最后Collectors.toList()将结果转为list类型return authorities;}......
}

这里的通过

authorities = permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

将permissions中的两个参数"test"、"admin"分别构造SimpleGrantedAuthority类,并且放入Collectors之中,最终返回构造的Collectors权限信息,取出来的UsernamePasswordAuthenticationToken和Authentication类分别为

authentication = UsernamePasswordAuthenticationToken [Principal=LoginUser(user=User(id=111110, userName=xiaomazai, nickName=xiaomazai, password=$2a$10$VDFx9Khqpo4FAkx/NZLL3uZO0PcBZekL3AU5JtzJuuxbn2emZUCUK, status=0, email=null, phonenumber=null, sex=null, avatar=null, userType=1, createBy=null, createTime=null, updateBy=null, updateTime=null, delFlag=0), permissions=[test, admin], authorities=[test, admin]), Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[test, admin]]

注意:这里如果调用authentication.toString()这个函数,就会调用LoginUser中的getAuthorities函数内容

(4)redis保存结果,返回登录成功

这里通过对userid的一个jwt加密创建出一个token,然后将token值保存到HashMap之中,最后返回登录成功响应

//如果认证通过,使用userId生成一个jwt,jwt存入ResponseResult返回
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
String userid = loginUser.getUser().getId().toString();
String jwt = JwtUtil.createJWT(userid);
//把完整的用户信息存入redis之中,userId作为key,
Map<String,String> map = new HashMap<>();
map.put("token",jwt);
//把完整的用户信息存入redis,userId作为key,这里面的键值对键为token值为jwt
redisCache.setCacheObject("login:"+userid,loginUser);
//这里必须打开redis,才能够保存得上
return new ResponseResult(200,"登录成功",map);

2.第二次登录调用

第二次登录的时候需要在headers之中加入token头,直接访问hello接口部分
访问hello部分接口
进入到doFilterInternal函数之中运行,首先获取token的内容,然后根据token解析出userId的内容

//1.获取token
String token = httpServletRequest.getHeader("token");
if(!StringUtils.hasText(token))
{filterChain.doFilter(httpServletRequest,httpServletResponse);//这里可以放行的原因在于后面还有FilterSecurityInterceptor等其他过滤器,//如果没有认证后面还会被拦截下来return;//这里如果没有return,响应回来之后还会调用后面的代码
}String userId;
//2.解析token(响应不为空)
try {Claims claims = JwtUtil.parseJWT(token);userId = claims.getSubject();//这里parseJWT就是一个解析过程,不需要过多深究
} catch (Exception e) {e.printStackTrace();throw new RuntimeException("token非法");
}

获取到userId之后,从redis中根据userId找寻到对应的LoginUser类别

String rediskey = "login:"+userId;
LoginUser loginUser = redisCache.getCacheObject(rediskey);
if(Objects.isNull(loginUser))
{throw new RuntimeException("用户未登录");
}

接下来将loginUser封装成为UsernamePasswordAuthenticationToken类

UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());

然后判断UsernamePasswordAuthentication是否能够通过安全认证

SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

这里由于hello接口上面标注了@PreAuthorize(“hasAuthority(‘test’)”),因此可以成功放行

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

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

相关文章

FinClip 支持创建 H5应用类小程序;PC 终端 优化升级

FinClip 的使命是使您能够通过小程序解决关键业务流程挑战&#xff0c;并完成数字化转型。不妨让我们看看本月产品与市场发布亮点&#xff0c;是否有助于您实现目标。 产品方面的相关动向&#x1f447;&#x1f447;&#x1f447; FinClip 支持创建 H5应用类小程序 近期我们…

MongoDB常用的比较符号和一些功能符号

比较符号 results collection.find({age: {$gt: 20}})功能符号 results collection.find({name: {$regex: ^M.*}})

怎么把pdf转换成高清图片?

怎么把pdf转换成高清图片&#xff1f;最近&#xff0c;我的同事遇到了一个问题&#xff0c;现在她需要将一些pdf文件转换成高清的图片&#xff0c;这件事情让让她感到非常无助&#xff0c;因为她非常着急需要将这些文件转换为图片格式&#xff0c;以便更好的在今后的工作中进行…

Ubuntu 22.04.2 LTS 安装python3.6后报错No module named ‘ufw‘

查明原因&#xff1a; vim /usr/sbin/ufw 初步判断是python版本的问题。 # 查看python3软链接 ll /usr/bin/python3 将python3的软链接从python3.6换成之前的3.10&#xff0c;根据自己电脑情况。 可以查看下 /usr/bin 下有什么 我这是python3.10 所以解决办法是 # 移除py…

工业互联网龙头企业研祥智能加入 openKylin

导读近日&#xff0c;研祥智能科技股份有限公司&#xff08;以下简称 “研祥智能”&#xff09;签署 openKylin 社区 CLA&#xff08;Contributor License Agreement 贡献者许可协议&#xff09;&#xff0c;正式加入 openKylin 开源社区。 研祥智能于 1993 年 12 月 31 日成立…

如何让insert程序速度快,可以试试联合SQL(insert 和 select 一起使用)?

查询添加可选择SQL执行&#xff0c;速度远超程序执行 insert 和 select案例 insert into 表1(列1,列2,列3,...) select 列1,列2,列3,...from表2(GROUP BY 列)116511 条数据 耗时45秒&#xff0c; 如果是程序查询然后再insert&#xff0c;则需要30分钟左右&#xff01;&#x…

Server - PyTorch BFloat16 “TypeError: Got unsupported ScalarType BFloat16“ 解决方案

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132665807 BFloat16 类型是 16 位的浮点数格式&#xff0c;可以用来加速深度学习的计算和存储。BFloat16 类型的特点是保留 32 位浮点数&#xff…

企业级智能PDF及文档处理SDK GdPicture.NET 14.2 Crack

企业级智能PDF及文档处理SDK GdPicture.NET 提供了一组非常先进的 API&#xff0c;这些 API 利用了人工智能、机器学习和模糊逻辑算法等尖端技术。经过超过 15 年的持续研究和对创新的专注&#xff0c;我们的 SDK 已成为市场上针对PDF、OCR、条形码、文档成像和各种格式最全面的…

Apache Linkis 与 OceanBase 集成:实现数据分析速度提升

导语&#xff1a;恭喜 OceanBase 生态全景图中又添一员&#xff0c;Apache Linkis 构建了一个计算中间件层&#xff0c;以促进上层应用程序和底层数据引擎之间的连接、治理和编排。 近日&#xff0c;计算中间件 Apache Linkis 在其新版本中通过数据源功能&#xff0c;支持用户通…

行军遇到各种复杂地形怎么处理?

行军遇到各种复杂地形怎么处理&#xff1f; 【安志强趣讲《孙子兵法》第30讲】 【原文】 凡军好高而恶下&#xff0c;贵阳而贱阴&#xff0c;养生而处实&#xff0c;军无百疾&#xff0c;是谓必胜。 【注释】 阳&#xff0c;太阳能照到的地方。阴&#xff0c;太阳照不到的地方。…

华为Mate 60系列安装谷歌服务框架,安装Play商店,Google

华为Mate 60 Pro悄悄的上架。但是却震撼市场的强势登场,Mate 60系列默认搭载的就是鸿蒙4.0。那么mate 60加上4.0是否可以安装谷歌服务框架呢&#xff1f;本机到手经过测试是可以安装的&#xff0c;但是在解决play非保护机制认证还通知这个问题上,他和鸿蒙3.0是不一样的。如果我…

Flink(java版)

watermark 时间语义和 watermark 注意:数据进入flink的时间&#xff1a;如果用这个作为时间语义就不存在问题&#xff0c;但是开发中往往会用处理时间 作为时间语义这里就需要考虑延时的问题。 如上图&#xff0c;数据从kafka中获取出来&#xff0c;从多个分区中获取&#xf…

ubuntu 22.04安装cuda、cudnn、conda、pytorch

1、cuda 视频连接 https://www.bilibili.com/video/BV1bW4y197Mo/?spm_id_from333.999.0.0&vd_source3b42b36e44d271f58e90f86679d77db7cuda 11.8 https://developer.nvidia.com/cuda-toolkit-archive点击进入 https://developer.nvidia.com/cuda-11-8-0-download-arc…

gRPC-GateWay Swagger 实战

上一次我们分享了关于 gRPC-Gateway 快速实战 &#xff0c;可以查看地址来进行回顾 : 也可以查看关于 gRPC 的历史文章&#xff1a; gRPC介绍 gRPC 客户端调用服务端需要连接池吗&#xff1f; gRPC的拦截器 gRPC的认证 分享一下 gRPC- HTTP网关 I 今天主要是分享关于 gRPC-G…

Linux相关指令(下)

cat指令 查看目标文件的内容 常用选项&#xff1a; -b 对非空输出行编号 -n 对输出的所有行编号 -s 不输出多行空行 一个重要思想&#xff1a;linux下一切皆文件&#xff0c;如显示器文件&#xff0c;键盘文件 cat默认从键盘中读取数据再打印 退出可以ctrlc 输入重定向<…

kubernetes/cluster/addons/fluentd-elasticsearch

#发文福利# 一、前言 kubernetes 1.23搭建EFK所用到的yaml文件&#xff0c;本帖均来自kubernetes官方&#xff0c;且没做修改。 https://github.com/kubernetes/kubernetes/tree/release-1.23/cluster/addons/fluentd-elasticsearch 二、EFK 原版yaml 1、create-logging-na…

机器学习笔记之最优化理论与方法(六)无约束优化问题——最优性条件

机器学习笔记之最优化理论与方法——无约束优化问题[最优性条件] 引言无约束优化问题无约束优化问题最优解的定义 无约束优化问题的最优性条件无约束优化问题的充要条件无约束优化问题的必要条件无约束优化问题的充分条件 引言 本节将介绍无约束优化问题&#xff0c;主要介绍无…

css让元素保持等比例宽高

使用新属性 aspect-ratio: 16/9; 代码示例 <style>div {width: 60%;/* 等比例宽高 */aspect-ratio: 16/9;background-color: red;margin: auto;}</style> </head><body><div></div> </body>示例 aspect-ratio兼容性

python-wordcloud词云

导入模块 from wordcloud import WordCloud import jieba import imageio import matplotlib.pyplot as plt from PIL import ImageGrab import numpy as npwordcloud以空格为分隔符号&#xff0c;来将文本分隔成单词 PIL pillow模块 img imageio.imread(image.png)这行代码…

软件测试—测试用例的设计

软件测试—测试用例的设计 测试用例是什么&#xff1f; 首先&#xff0c;测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试系统提供的一组集合。这组集合包括&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。 好的测试用例的特征 一个好的测试…