【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;本文主要介绍后端扩展开发方式&…

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;在 训练样集上的精度是单调上升的&…

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数据库使用优化…

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

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

【业务功能篇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…

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…

webpack5 (三)

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

APS系统设计经验分享(时间推导II - 2023.09)

在前一篇关于APS系统设计分享文章(《APS系统设计经验分享(时间推导 - 2023.03)》)中&#xff0c;我们提到将会分享使用OptaPlanner作为规划引擎开发APS系统过程中&#xff0c;遇到的一些时间相关的设计建议与异常情况分析。后来一直忙于项目工作&#xff0c;直到现在才想起仍欠…

CC-TAIX01 HONEYWELL 霍尼韦尔连接工厂热智商远程监测系统

CC-TAIX01 HONEYWELL 霍尼韦尔连接工厂热智商远程监测系统 -霍尼韦尔宣布霍尼韦尔连接工厂热智商,一种基于云的远程监测系统,旨在监测和管理关键的热过程数据。这是霍尼韦尔资产绩效管理(APM)投资组合的一部分。 热智商是工业和商业热应用的远程监测解决方案。它将燃烧设备连…

Flink基础实操-计算单词出现次数

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

【云原生】容器编排工具Kubernetes

目录 一、 K8S介绍 官网地址&#xff1a; 1.1docker编排与k8s编排相比 1.2特性 1.3功能 二、K8S重要组件 2.1核心组件 &#xff08;1&#xff09;Kube-apiserver &#xff08;2&#xff09;Kube-controller-manager &#xff08;3&#xff09;Kube-scheduler &#x…

DevOps管理软件生命周期

整体的软件开发流程 PLAN&#xff1a;开发团队根据客户的目标制定开发计划 CODE&#xff1a;根据PLAN开始编码过程&#xff0c;需要将不同版本的代码存储在一个库中。GIT,SVN BUILD&#xff1a;编码完成后&#xff0c;需要将代码构建并且运行。MAVEN TEST&#xff1a;成功构建…

【性能优化】聊聊性能优化那些事

针对于互联网应用来说&#xff0c;性能优化其实就是一直需要做的事情&#xff0c;因为系统响应慢&#xff0c;是非常影响用户的体验&#xff0c;可能回造成用户流失。所以对于性能非常重要。最近正好接到一个性能优化的需求&#xff0c;需要对所负责的系统进行性能提升。目前接…

Windows环境下RabbitMQ下载安装

一、准备安装文件 1、下载Erlang 登录网站Downloads - Erlang/OTP&#xff0c;选择“Download Windows installer”&#xff0c;如下图所示&#xff1a; 弹出框中&#xff0c;选在下载保存地址&#xff0c;保存文件&#xff0c;如下图所示&#xff1a; 2、下载RabbitMQ 登录…

ElasticSearch简介

一、基本概念 1、Index&#xff08;索引&#xff09; 动词&#xff0c;相当于 MySQL 中的 insert&#xff1b; 名词&#xff0c;相当于 MySQL 中的 Database 2、Type&#xff08;类型&#xff09; 在 Index&#xff08;索引&#xff09;中&#xff0c;可以定义一个或多个类…