【SpringSecurity】十一、SpringSecurity集成JWT实现token的方法与校验

文章目录

  • 1、依赖与配置
  • 2、JWT工具类
  • 3、认证成功处理器
  • 4、创建JWT过滤器
  • 5、安全配置类

1、依赖与配置

添加JWT的maven依赖:

<!-- 添加jwt的依赖 -->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.11.0</version>
</dependency>

application.yaml中配置密钥的值,方便代码中引用和后续更改:

jwt:secretKey: mykey

2、JWT工具类

这里的命名改为JWTService好点,Utils命名似乎偏静态方法一点。

@Component
@Slf4j
public class JwtUtils {//算法密钥@Value("${jwt.secretKey}")private String jwtSecretKey;/*** 创建jwt** @param userInfo 用户信息* @param authList 用户权限列表* 根据登录用户的数据库信息和权限信息,加上服务端密钥,创建token* @return 返回jwt(JSON WEB TOKEN)*/public String createToken(String userInfo, List<String> authList) {//创建时间Date currentTime = new Date();//过期时间,5分钟后过期Date expireTime = new Date(currentTime.getTime() + (1000 * 60 * 5));//jwt的header信息Map<String, Object> headerClaims = new HashMap<>();headerClaims.put("type", "JWT");headerClaims.put("alg", "HS256");//创建jwtreturn JWT.create().withHeader(headerClaims) // 头部信息.withIssuedAt(currentTime) //已注册声明:签发日期,发行日期.withExpiresAt(expireTime) //已注册声明 过期时间.withIssuer("llg")  //已注册声明,签发人.withClaim("userInfo", userInfo) //私有声明,可以自己定义.withClaim("authList", authList) //私有声明,可以自定义.sign(Algorithm.HMAC256(jwtSecretKey)); // 签名,使用HS256算法签名,并使用密钥//HS256是一种对称算法,这意味着只有一个密钥,在双方之间共享。 使用相同的密钥生成签名并对其进行验证。 应特别注意钥匙是否保密。}/*** 验证jwt的签名,简称验签* @param token 需要验签的jwt* @return 验签结果*/public boolean verifyToken(String token) {//获取验签类对象JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();try {//验签,如果不报错,则说明jwt是合法的,而且也没有过期DecodedJWT decodedJWT = jwtVerifier.verify(token);return true;} catch (JWTVerificationException e) {//如果报错说明jwt 为非法的,或者已过期(已过期也属于非法的)log.error("验签失败:{}", token);e.printStackTrace();}return false;}/*** 从token中获取用户信息* 这个userInfo是创建token时我自己塞进去的* @param token jwt* @return 用户信息*/public String getUserInfo(String token) {//创建jwt验签对象JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();try {//验签(主要为了同时的获取解析的结果)DecodedJWT decodedJWT = jwtVerifier.verify(token);//获取payload中userInfo的值,并返回return decodedJWT.getClaim("userInfo").asString();} catch (JWTVerificationException e) {e.printStackTrace();}return null;}/*** 获取用户权限** @param token* @return*/public List<String> getUserAuth(String token) {//创建jwt验签对象JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSecretKey)).build();try {//验签(主要为了同时的获取解析的结果)DecodedJWT decodedJWT = jwtVerifier.verify(token);//获取payload中的自定义数据authList(权限列表),并返回return decodedJWT.getClaim("authList").asList(String.class);} catch (JWTVerificationException e) {e.printStackTrace();}return null;}}

再贴一下下统一结果类的定义:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class HttpResult implements Serializable {private Integer code; //响应码private String msg; //响应消息private Object data; //响应对象
}

下面是安全用户类,用于在数据库的用户对象类SysUser和返给框架的官方对象类UserDetails之间做过渡转换。UserDetails <====> SecurityUser <====> SysUser

@Setter
public class SecurityUser implements UserDetails {private  final SysUser sysUser;private List<SimpleGrantedAuthority> simpleGrantedAuthorities;public SecurityUser(SysUser sysUser) {this.sysUser=sysUser;}public SysUser getSysUser() {return sysUser;
}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return simpleGrantedAuthorities;}@Overridepublic String getPassword() {String userPassword=this.sysUser.getPassword();//注意清除密码this.sysUser.setPassword(null);return userPassword;}@Overridepublic String getUsername() {return sysUser.getUsername();}@Overridepublic boolean isAccountNonExpired() {return sysUser.getAccountNoExpired().equals(1);}@Overridepublic boolean isAccountNonLocked() {return sysUser.getAccountNoLocked().equals(1);}@Overridepublic boolean isCredentialsNonExpired() {return sysUser.getCredentialsNoExpired().equals(1);}@Overridepublic boolean isEnabled() {return sysUser.getEnabled().equals(1);}
}

3、认证成功处理器

自定义处理器,实现AuthenticationSuccessHandler,当用户登录认证成功后,会执行这个处理器,即认证成功处理器

/*** 认证成功处理器,当用户登录成功后,会执行此处理器*/
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {//使用此工具类进行序列化@Resourceprivate ObjectMapper objectMapper;@Resourceprivate JwtUtils jwtUtils;@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {//从认证对象中获取认证用户信息//查看前面第六章的UserDetailsService接口的loadUserByUsername方法,//返回给框架的是一个自定义的SecurityUser对象(Security实现了UserDetails)SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();//从SecurityUser中拿出和底层MYSQL挂钩的SysUser类信息String userInfo=objectMapper.writeValueAsString(securityUser.getSysUser());List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) securityUser.getAuthorities();//List<SimpleGrantedAuthority>转List<String>List<String> authList=new ArrayList<>();for (SimpleGrantedAuthority authority : authorities) {authList.add(authority.getAuthority());}//也可使用stream流代替上面的for循环List<String> authList = authorities.stream().map(a -> {return a.getAuthority();}).collect(Collectors.toList());//也可使用stream流+Lambda表达式List<String> authList = authorities.stream().map(SimpleGrantedAuthority::getAuthority).collect(Collectors.toList());// 调用前面的JWT工具类方法创建jwtString token = jwtUtils.createToken(userInfo,authList);//返回给前端token@Builder模式创建对象HttpResult httpResult = HttpResult.builder().code(200).msg("OK").data(token).build();response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.write(objectMapper.writeValueAsString(httpResult));writer.flush();}
}

4、创建JWT过滤器

定义JWT过滤器,用来检验一个个对接口的请求中token是否合法,注意放行登录接口:

/*** 定义一次性请求过滤器*/
@Component
@Slf4j
public class JwtCheckFilter extends OncePerRequestFilter {@Resourceprivate ObjectMapper objectMapper;@Resourceprivate JwtUtils jwtUtils;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {//获取请求uriString requestURI = request.getRequestURI();// 如果是登录页面,放行if (requestURI.equals("/login")) {filterChain.doFilter(request, response);return;}//获取请求头中的Authorization,前端一般这么传,key为AuthorizationString authorization = request.getHeader("Authorization");//如果Authorization为空,那么不允许用户访问,直接返回if (!StringUtils.hasText(authorization)) {printFront(response, "没有登录!");return;}//Authorization 去掉头部的Bearer 信息,获取token值String jwtToken = authorization.replace("Bearer ", "");//验签boolean verifyTokenResult = jwtUtils.verifyToken(jwtToken);//验签不成功if (!verifyTokenResult) {printFront(response, "jwtToken 已过期");return;}//到这儿算是验证通过,但还没结束,还要将信息填充后返给SpringSecurity框架//从payload中获取userInfoString userInfo = jwtUtils.getUserInfo(jwtToken);//从payload中获取授权列表List<String> userAuth = jwtUtils.getUserAuth(jwtToken);//在认证成功处理器中,创建token时,userInfo里放的是SysUser对象的序列化字符串,这里反序列化SysUser sysUser = objectMapper.readValue(userInfo, SysUser.class);SecurityUser securityUser = new SecurityUser(sysUser);//设置权限List<SimpleGrantedAuthority> authList = userAuth.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());securityUser.setAuthorityList(authList);//填充信息UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToke = new UsernamePasswordAuthenticationToken(securityUser, null, authList);//通过安全上下文设置认证信息SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToke);//继续访问相应的rul等filterChain.doFilter(request, response);}/*** 定义一个通过response向前端返回数据的方法* 这里不是controller层,不是你直接返回个结果类就行的,注意区别*/private void printFront(HttpServletResponse response, String message) throws IOException {response.setCharacterEncoding("UTF-8");response.setContentType("application/json;charset=utf-8");PrintWriter writer = response.getWriter();HttpResult httpResult = new HttpResult();httpResult.setCode(401);httpResult.setMsg(message);writer.print(objectMapper.writeValueAsString(httpResult));writer.flush();}
}

上面的过滤器中,除了正常的验签,最后的消息填充与保存在安全上下文,就是下图中的第十步:

在这里插入图片描述

5、安全配置类

修改下安全配置类,把上面的处理器和过滤器加进来。

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Resourceprivate MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;@Resourceprivate JwtCheckFilter jwtCheckFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {//先看token是否合法,再走框架的用户名密码校验过滤器http.addFilterBefore(jwtCheckFilter, UsernamePasswordAuthenticationFilter.class);//要是有之间的验证码校验,则它应该在token校验之前//认证通过后,走认证成功处理器,颁发tokenhttp.formLogin().successHandler(myAuthenticationSuccessHandler).permitAll();//简单按接口加个权限要求http.authorizeRequests().mvcMatchers("/student/**").hasAnyAuthority("student:query","student:update").anyRequest().authenticated(); //任何请求均需要认证(登录成功)才能访问http.csrf().disable();  //跨域//禁用session方式http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);}}

效果:
在这里插入图片描述

登录认证后返给前端token:

在这里插入图片描述

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

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

相关文章

NC后端扩展开发

前言 在日常的工作中&#xff0c;会遇到各种各样的需要进行扩展开发的需求&#xff0c;可以使用系统预留的扩展开发机制来实现&#xff0c;避免修改源码。因NC产品已迭代至BIP版本&#xff0c;所以前端扩展方式就再进行不赘述了&#xff0c;本文主要介绍后端扩展开发方式&…

50个Linux常用命令行快捷键(大部分适配Mac OS)

50个Linux常用命令行快捷键 &#xff08;大部分适配Mac OS&#xff09; 移动光标到行首&#xff1a;Ctrl a移动光标到行尾&#xff1a;Ctrl e移动光标到上一个单词的开头&#xff1a;Ctrl ←移动光标到下一个单词的开头&#xff1a;Ctrl →删除光标之前的字符&#xff1a;C…

sentinel blockHandler不生效

sentinel blockHandler不生效: package org.bc.sentinel.controller;import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.apache.commons.lang3.RandomUtils; import org.springfram…

LeetCode 47题:全排列2

题目 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2],[1,2,1],[2,1,1]]示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[…

机器学习---预剪枝、后剪枝(REP、CCP、PEP、)

1. 为什么要进行剪枝 横轴表示在决策树创建过程中树的结点总数&#xff0c;纵轴表示决策树的预测精度。 实线显示的是决策树 在训练集上的精度&#xff0c;虚线显示的则是在⼀个独⽴的测试集上测量出来的精度。 随着树的增⻓&#xff0c;在 训练样集上的精度是单调上升的&…

缓存的放置时间和删除时间

缓存的放置时间和删除时间是指缓存中存储的数据的生命周期。这两个时间点非常重要&#xff0c;因为它们决定了缓存数据的有效期和何时应该从缓存中删除。 缓存的放置时间&#xff08;Cache Put Time&#xff09;&#xff1a;这是指数据首次放入缓存的时间点。当数据被放入缓存时…

Docker安装部署ElasticSearch(ES)

文章目录 安装前准备创建挂载目录授权相关权限创建elasticsearch.yml文件 拉取镜像运行容器查看运行情况测试 安装前准备 创建挂载目录 用于在宿主机挂载日志&#xff0c;数据等内容 创建/opt/es/data目录 创建/opt/es/logs目录 创建/opt/es/plugins目录 创建/opt/es/conf目录…

MYSQL调优之思路----sql语句和索引调优

MySQL数据库性能优化包括综合多方面因素&#xff0c;应根据实际的业务情况制定科学、合理的调优方案进行测试调优 文章目录 MySQL性能优化1 优化介绍1.2 优化要考虑的问题2.1 优化可能带来的问题2.2 优化的需求2.3 优化由谁参与2.4 优化的方向2.5 优化的维度 1.2数据库使用优化…

node基础之七:Mongodb 数据库

下载地址&#xff1a;https://www.mongodb.com/try/download/community v&#xff1a;5.0.20 platform&#xff1a;window package&#xff1a;zip 复制到 c 盘/Programs Files c 盘创建 data/db 文件夹 默认存放数据地址 在 bin 目录下启动数据库 mongod, 客户端连接数据库…

【力扣周赛】第 361 场周赛(⭐前缀和+哈希表 树上倍增、LCA⭐)

文章目录 竞赛链接Q1&#xff1a;7020. 统计对称整数的数目竞赛时代码——枚举预处理 Q2&#xff1a;8040. 生成特殊数字的最少操作&#xff08;倒序遍历、贪心&#xff09;竞赛时代码——检查0、00、25、50、75 Q3&#xff1a;2845. 统计趣味子数组的数目竞赛时代码——前缀和…

Rust的derive思考

这几天在Yew的学习实践中&#xff0c;发现derive中的参数中包含了yew自己的东西&#xff0c;比如yew::Properties。习惯使用#[derive(Clone, Debug, PartialEq)]之后&#xff0c;发现还有新的成员&#xff0c;这让我好奇起来。 首先让我们来回顾一下derive是什么。 #[derive(…

【黄啊码】nginx如何设置php运行的

禁止访问 PHP 脚本可以通过 Nginx 服务器配置中的多种方式来实现。以下是其中的一些常见方法&#xff0c;您可以根据实际需求选择合适的方式&#xff1a; 1 禁用 PHP 解析&#xff1a; 在 Nginx 配置中&#xff0c;确保 PHP 脚本无法被解析&#xff0c;从而禁止 PHP 执行。 lo…

【业务功能篇96】微服务-springcloud-springboot-认证服务-登录注册功能-Auth2.0-分布式session

5.登录功能 通过最基础的登录操作来完成登录处理 登录页面处理 认证服务的处理 /*** 注册的方法* return*/PostMapping("/login")public String login(LoginVo loginVo , RedirectAttributes redirectAttributes){R r memberFeginService.login(loginVo);if(r.getC…

thinkphp6 入门(5)-- 模型是什么 怎么用

一、模型 MVC架构 之前开发一个功能&#xff0c;后端为在控制器&#xff08;C&#xff09;中写 php SQL&#xff0c;前端为在页面&#xff08;V&#xff09;中写html css js&#xff0c;这就形成了 VC 架构。 但是发现&#xff0c;相同的数据逻辑&#xff08;SQL&#xf…

Centos、OpenEuler系统安装mysql

要在CentOS上安装MySQL并设置开机自启和root密码&#xff0c;请按照以下步骤进行操作&#xff1a; 确保您的CentOS系统已连接到Internet&#xff0c;并且具有管理员权限&#xff08;root或sudo访问权限&#xff09;。打开终端或SSH会话&#xff0c;使用以下命令安装MySQL&…

IntelliJ IDEA的远程开发(Remote Development)

DEA的远程开发功能&#xff0c;可以将本地的编译、构建、调试、运行等工作都放在远程服务器上执行&#xff0c;而本地仅运行客户端软件进行常规的开发操作即可&#xff0c;官方给出的逻辑图如下&#xff0c;可见通过本地的IDE和服务器上的IDE backend将本地电脑和服务器打通&am…

达梦8 在CentOS 系统下静默安装

确认系统参数 [rootlocalhost ~]# ulimit -a core file size (blocks, -c) unlimited data seg size (kbytes, -d) unlimited【1048576(即 1GB)以上或 unlimited】 scheduling priority (-e) 0 file size (blocks, -f) unlimite…

三、视频设备的枚举以及插拔检测

一、前言 本章主要讲述&#xff0c;如何获取设备名称以及guid&#xff0c;采集设备的采集格式识别&#xff0c;设备的插拔 设备列表以及属性的获取使用的directshow(后续的MediaFoundation无法获取OBS摄像头) 设备的插拔使用的是QT 捕获系统消息&#xff0c;捕获到设备插拔后&a…

day6_网络编程

网络聊天室 客户端 main.c #include "include/errorAndHead.h"void hander(int sig) {while (waitpid(-1, NULL, WNOHANG) > 0); }int main(int argc, const char *argv[]) {/* 捕获 SIGCHLD 信号 hander 处理*/if (signal(SIGCHLD, hander) SIG_ERR){ERR_MSG…

webpack5 (三)

webpack 高级配置 其实就是对 webpack 进行优化&#xff0c;让代码在编译/运行时性能更好 1. 提升开发体验 2. 提升打包构建速度 3. 减少代码体积 4. 优化代码运行性能 一、提升开发体验 sourcemap 在编译打包后所有的 css 和 js 都合并为了一个文件&#xff0c;并多了很多…