【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.hqwc.cn/news/98553.html

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

相关文章

vue 使用qrcode生成二维码并可下载保存

安装qrcode npm install qrcode --save代码 <template><div style"display: flex; flex-direction: column; align-items: center; justify-content center;"><div>查看溯源码&#xff0c;<a id"saveLink" style"text-decorati…

C#,《小白学程序》第十四课:随机数(Random)第一,几种随机数的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十四课&#xff1a;随机数&#xff08;Random&#xff09;第一&#xff0c;几种随机数的计算方法与代码 /// 本课初步接触一下随机数。 /// </summary> /// <param name"sender"></param> ///…

开发前期准备工作

开发前期准备工作 文章目录 开发前期准备工作0 代码规范0.1 强制0.2 推荐0.3 参考dao&#xff1a;跟数据库打交道service&#xff1a;业务层&#xff0c;人类思维解决controller&#xff1a;抽象化 0.4 注释规范0.5 日志规范0.6 专有名词0.7 控制层统一异常统一结构体控制层提示…

【git】从一个git仓库迁移到另外一个git仓库

在远端服务器创建一个新的仓库 用界面创建&#xff0c;当然也可以用命令创建 拉去源仓库 git clone --bare git192.168.10.10:java/common.gitgit clone --bare <旧仓库地址>拉去成功以后会出现 进入到文件夹内部 出现下面信息&#xff1a; 推送到新的远端仓库 git …

Java23种设计模式之【单例模式】

目录 一.单例模式的起源&#xff0c;和应用场景 1.单例模式的前世今生&#xff01; 2.什么是单例模式&#xff1f; 2.1使用单例模式的注意事项 2.2如何理解单例模式&#xff1f; 2.3单例模式的优势以及不足&#xff01; 2.4使用场景 二.实现 1.实现思路 1.1创建一个 S…

详细说明OSPF常见的LSA

目录 1类LSA &#xff08;Router LSA&#xff09;介绍 总结&#xff1a;1类LSA 2类LSA &#xff08;Network LSA&#xff09;介绍 总结&#xff1a;2类LSA 3类LSA &#xff08;Summary LSA&#xff09;介绍 总结&#xff1a;3类LSA 5类LSA &#xff08;ase LSA&…

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

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

Qt--自定义搜索控件,QLineEdit带前缀图标

写在前面 这里自定义一个搜索控件&#xff0c;通过自定义LineEdit的textChange信号&#xff0c;搜索指定内容&#xff0c;并以QCheckBox的方式显示在QListWidget中。 开发版本 Qt: 5.15.2 Qt: Creator10.0.2 编译环境&#xff1a;msvc2019_64bit release 效果 代码 自定义…

【真题解析】系统集成项目管理工程师 2023 年上半年真题卷(综合知识)

本文为系统集成项目管理工程师考试(软考) 2023 年上半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析(问答题 4 道,75分)综合知识(选择题*75)1-10 题11-20 题21-30 题31-40 题41-50 题51-60 …

【广州华锐互动】VR党建多媒体互动展厅:随时随地开展党史教育

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐渗透到各个领域&#xff0c;其中党建教育尤为受益。为了更好地传承红色基因&#xff0c;弘扬党的优良传统&#xff0c;广州华锐互动推出了VR党建多媒体互动展厅&#xff0c;让广大党员干部和人民群众通过现代科技手段&a…

OpenGL ES视频特效开发参考Shadertoy参数详解参考Godot文档

今天一个大厂的学员过来问shadertoy上一些参数的问题&#xff0c;因为我之前用过一段时间Godot引擎&#xff0c; 我清晰记得Godot官方文档有明确的解释&#xff0c;所以整理下发给做特效的同学。 Shadertoy是一个网站&#xff0c;它方便用户编写片段着色器并创造出纯粹的魔法。…

15种下载文件的方法文件下载方法汇总超大文件下载

15种下载文件的方法&文件下载方法汇总&超大文件下载 15种下载文件的方法Pentesters经常将文件上传到受感染的盒子以帮助进行权限提升&#xff0c;或者保持在计算机上的存在。本博客将介绍将文件从您的计算机移动到受感染系统的15种不同方法。对于那些在盒子上存在且需要…