当前位置: 首页 > news >正文

security第十六集 引入JWT

JWT(JSON Web Token) 是一种紧凑的、自包含的、用于在各方之间安全传输信息的 JSON 对象格式。它通常用于身份认证和信息交换。

一个 JWT 由三部分组成,用 . 连接
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
────────── HEADER ────────── ─────────── PAYLOAD ──────────── ──────── SIGNATURE ────────
Header(头部)

{"alg":"HS256",// 签名算法"typ":"JWT"// Token 类型}

Payload(载荷/数据体)

{// 标准字段(Registered Claims)"iss":"auth-server",// 签发者"sub":"1234567890",// 主题(通常是用户ID)"aud":"order-service",// 受众"exp":1735689600,// 过期时间"iat":1735686000,// 签发时间// 自定义字段(Public/Private Claims)"userId":"user-001","username":"zhangsan","role":"ADMIN","permissions":["read","write"]}

Signature(签名)

```javaHMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

JWT工作原理

简单了解 jwt 之后 ,我们根据上两集的多因素认证的 认证服务和客户端进行 一下 改造

  • 首先修改一下认证服务

1.加入jwt所需maven依赖

<!--JWT依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.5</version><scope>runtime</scope></dependency>

2.创建jwtUtil

@ComponentpublicclassJwtUtil{//这个需自己定义,我用的 HS256,所以密钥至少要 32 个字符privatestaticfinalStringSECRET_KEY="a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6";privatestaticfinallongEXPIRATION_TIME=24*60*60*1000;// 24小时privateSecretKeygetSigningKey(){returnKeys.hmacShaKeyFor(SECRET_KEY.getBytes());}/** * 生成 JWT Token */publicStringgenerateToken(Stringusername,StringuserId){Map<String,Object>claims=newHashMap<>();claims.put("userId",userId);claims.put("username",username);returnJwts.builder().setClaims(claims).setSubject(username).setIssuedAt(newDate()).setExpiration(newDate(System.currentTimeMillis()+EXPIRATION_TIME)).signWith(getSigningKey(),SignatureAlgorithm.HS256).compact();}/** * 验证 JWT Token */publicbooleanvalidateToken(Stringtoken){try{Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);returntrue;}catch(JwtException|IllegalArgumentExceptione){returnfalse;}}/** * 从 Token 中获取用户名 */publicStringgetUsernameFromToken(Stringtoken){Claimsclaims=Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.getSubject();}/** * 从 Token 中获取用户ID */publicStringgetUserIdFromToken(Stringtoken){Claimsclaims=Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.get("userId",String.class);}}

3.修改UserService中的userAuth 方法,返回jwt

//验证密码是否匹配if(passwordEncoder.matches(user.getPassword(),u.getPassword())){//密码匹配后生成 JWT TokenStringtoken=jwtUtil.generateToken(u.getUsername(),u.getId());returntoken;}else{thrownewBadCredentialsException("Bad credentials");}

4.修改AuthController的/user/auth方法返回token

@PostMapping("/user/auth")publicMap<String,String>userAuth(@RequestBodyUseruser){Stringtoken=userService.userAuth(user);Map<String,String>map=newHashMap<>();map.put("token",token);returnmap;}
  • 然后我们修改business-service

1.pom文件引入上面的jwt依赖
2.创建JwtUtil和上诉一样
3.修改AuthenticationServerFacade的checkPassword方法,获取返回token

publicStringcheckPassword(Stringusername,Stringpassword){Stringurl=baseUrl+"/user/auth";Useruser=newUser();user.setUsername(username);user.setPassword(password);HttpEntity<User>request=newHttpEntity<>(user);ResponseEntity<Map<String,String>>response=restTemplate.exchange(url,HttpMethod.POST,request,newParameterizedTypeReference<Map<String,String>>(){});returnresponse.getBody().get("token");}

4.创建JwtAuthenticationFilter来验证jwt

@ComponentpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{@AutowiredprivateJwtUtiljwtUtil;@OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{Stringheader=request.getHeader("Authorization");if(header!=null&&header.startsWith("Bearer ")){Stringtoken=header.substring(7);if(jwtUtil.validateToken(token)){Stringusername=jwtUtil.getUsernameFromToken(token);Stringuserid=jwtUtil.getUserIdFromToken(token);UsernamePasswordAuthenticationTokenauthentication=newUsernamePasswordAuthenticationToken(username,null,Collections.emptyList());authentication.setDetails(userid);SecurityContextHolder.getContext().setAuthentication(authentication);}}filterChain.doFilter(request,response);}@OverrideprotectedbooleanshouldNotFilter(HttpServletRequestrequest){returnrequest.getServletPath().equals("/login");}}

5.创建LoginController实现/login方法

@RestControllerpublicclassLoginController{@AutowiredprivateAuthenticationServerFacadeauthenticationServerFacade;/** * 登录接口 - 门面模式 * 客户端只和 Business-Service 交互 */@PostMapping("/login")publicResponseEntity<Map<String,String>>login(@RequestBodyMap<String,String>credentials){Stringusername=credentials.get("username");Stringpassword=credentials.get("password");// 内部调用 Security-Service 获取 JWT TokenStringtoken=authenticationServerFacade.checkPassword(username,password);Map<String,String>response=newHashMap<>();response.put("token",token);returnResponseEntity.ok(response);}}

6.修改 SecurityConfig类的 configure(HttpSecurity http)方法,引入jwt过滤器并放行/login

@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.csrf().disable();http.addFilterBefore(jwtAuthenticationFilter,BasicAuthenticationFilter.class);http.authorizeRequests().antMatchers("/login").permitAll().anyRequest().authenticated();}

好,现在我们可以测试访问了,我的 business-service端口号是8124

可以看到返回信息中已经有了 token,这个就是JWT

当我们再次访问business-service中的其他业务时就可以携带这个token值去访问
假设我们在business-service中在创建一个 controller来测试一下

@RestControllerpublicclassUserController{@AutowiredprivateAuthenticationServerFacadeauthenticationServerFacade;@GetMapping("/getUserInfoByToken")publicJSONObjectgetOrders(){Stringusername=(String)SecurityContextHolder.getContext().getAuthentication().getPrincipal();StringuserId=(String)SecurityContextHolder.getContext().getAuthentication().getDetails();JSONObjectresult=newJSONObject();result.put("username",username);result.put("userId",userId);result.put("message","获取用户信息成功");returnresult;}}

现在我们用postman测试一下这个接口,注意Authorization中要选择 BearerToken,并传入刚才生成的token值

看结果,已经能把jwt中的userId和username解析出来了

如果要是想更完善一些,可以在认证服务中 创建返回用户信息接口/user/info

@RestControllerpublicclassAuthController{@AutowiredprivateUserServiceuserService;//基于用户名和密码的认证,返回token@PostMapping("/user/auth")publicMap<String,String>userAuth(@RequestBodyUseruser){Stringtoken=userService.userAuth(user);Map<String,String>map=newHashMap<>();map.put("token",token);returnmap;}@GetMapping("/user/info")publicUserDTOgetUserInfo(@RequestParamStringusername){returnuserService.getUserByUsername(username);}}

然后再 business-service中的UserController中添加 getUserInfo方法

@GetMapping("/getUserInfo")publicUsergetUserInfo(){Stringusername=(String)SecurityContextHolder.getContext().getAuthentication().getPrincipal();Useruser=authenticationServerFacade.getUserInfo(username);returnuser;}

在AuthenticationServerFacade类中添加对应方法

/** * 调用认证中心获取用户详细信息 */publicUsergetUserInfo(Stringusername){Stringurl=baseUrl+"/user/info?username="+username;ResponseEntity<User>response=restTemplate.getForEntity(url,User.class);returnresponse.getBody();}

此时我们用postman在测试一下


可以看到 返回的user用户信息已经和 上两集的 数据信息完全对应上了!
至此jwt已经引入完毕

http://www.gsyq.cn/news/1562938.html

相关文章:

  • Adobe-GenP 3.0终极激活指南:5分钟解锁Adobe全系列软件的专业解决方案
  • 抖音视频批量下载终极指南:5分钟快速上手高效工具
  • 嵌入式GUI开发实战:emWin初始化配置与硬件加速优化详解
  • 长沙全屋定制工艺揭秘!高性价比背后究竟藏着什么秘诀? - 资讯速览
  • 2026年服务口碑好英国留学机构:五家优选深度解析 - 科技焦点
  • 2026年绍兴市CPPM考试最新全攻略:科目题型、通过率、备考重点及官方双认证报考机构推荐 - 众智商学院课程中心
  • 口碑超棒!长沙全屋定制优惠来袭,错过再等一年! - 资讯速览
  • [数智金融][14]金融桌面助手的设计和实现
  • 长沙全屋定制工艺大对比,专业视角带你一探究竟! - 资讯速览
  • 2026年东莞工业胶粘制品选购指南:EVA泡棉、硅胶垫、保护膜、双面胶、绒布垫配套厂商优选指南 - 海棠依旧大
  • Windows HEIC缩略图插件:5分钟快速解决iPhone照片预览难题的终极方案
  • 多场景低压配电母线槽应用方案,适配高安全标准电气工程
  • 2026年江苏GEO优化服务商实力榜单|本地企业生成式搜索优化首选指南 - 936品牌测评网
  • 想做专业长沙全屋定制?这些优质之选不容错过! - 资讯速览
  • 抖店新手怎么选拍单工具?筛选标准 + 避坑全指南 - 抖掌柜
  • 如何高效使用开源网盘直链下载助手:专业用户的实战指南
  • 终极指南:5步免费绕过iOS 15-16激活锁,解锁你的iPhone/iPad设备
  • 黄山学院的整体就业率怎么样?王牌专业的就业率能达到多少? - 寻茫精选
  • 终极免费解决方案:如何用novideo_srgb轻松校准NVIDIA显卡广色域显示器色彩
  • Android Studio中文界面插件:让开发工具说你的母语
  • 2026年常州货架厂口碑排行,这几家值得推荐 - 官方资讯
  • 2026 年合肥高科经济技工学校招生简章|报名方式、招生专业、录取条件详解 - 教育为先
  • 自动驾驶PPO训练实战:从Mujoco到CARLA的闭环落地
  • (开源)MotorEffMAP-电机电控效率MAP图绘制程序
  • 嵌入式GUI开发:emWin树形视图控件核心API与实战应用
  • YOLOv8车辆损伤检测与事故严重程度分级系统
  • 嵌入式GUI开发:LISTVIEW控件从入门到精通,实现高效数据展示与排序
  • 8GB显存跑35B大模型:Qwen-A3B轻量化部署实战
  • 【Netty源码解读和权威指南】第38篇:Netty SSL TLS安全传输——HTTPS背后的Netty实现
  • 嵌入式GUI开发:emWin 2D图形库核心功能与性能优化实战