JWTJSON Web Token 是一种紧凑的、自包含的、用于在各方之间安全传输信息的 JSON 对象格式。它通常用于身份认证和信息交换。一个 JWT 由三部分组成用 . 连接eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c────────── HEADER ────────── ─────────── PAYLOAD ──────────── ──────── SIGNATURE ────────Header头部{alg:HS256,// 签名算法typ:JWT// Token 类型}Payload载荷/数据体{// 标准字段Registered Claimsiss:auth-server,// 签发者sub:1234567890,// 主题通常是用户IDaud:order-service,// 受众exp:1735689600,// 过期时间iat:1735686000,// 签发时间// 自定义字段Public/Private ClaimsuserId:user-001,username:zhangsan,role:ADMIN,permissions:[read,write]}Signature签名javaHMACSHA256(base64UrlEncode(header).base64UrlEncode(payload),secret)JWT工作原理简单了解 jwt 之后 我们根据上两集的多因素认证的 认证服务和客户端进行 一下 改造首先修改一下认证服务1.加入jwt所需maven依赖!--JWT依赖--dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-api/artifactIdversion0.11.5/version/dependencydependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-impl/artifactIdversion0.11.5/versionscoperuntime/scope/dependencydependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-jackson/artifactIdversion0.11.5/versionscoperuntime/scope/dependency2.创建jwtUtilComponentpublicclassJwtUtil{//这个需自己定义我用的 HS256所以密钥至少要 32 个字符privatestaticfinalStringSECRET_KEYa1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6;privatestaticfinallongEXPIRATION_TIME24*60*60*1000;// 24小时privateSecretKeygetSigningKey(){returnKeys.hmacShaKeyFor(SECRET_KEY.getBytes());}/** * 生成 JWT Token */publicStringgenerateToken(Stringusername,StringuserId){MapString,ObjectclaimsnewHashMap();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){ClaimsclaimsJwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.getSubject();}/** * 从 Token 中获取用户ID */publicStringgetUserIdFromToken(Stringtoken){ClaimsclaimsJwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.get(userId,String.class);}}3.修改UserService中的userAuth 方法返回jwt//验证密码是否匹配if(passwordEncoder.matches(user.getPassword(),u.getPassword())){//密码匹配后生成 JWT TokenStringtokenjwtUtil.generateToken(u.getUsername(),u.getId());returntoken;}else{thrownewBadCredentialsException(Bad credentials);}4.修改AuthController的/user/auth方法返回tokenPostMapping(/user/auth)publicMapString,StringuserAuth(RequestBodyUseruser){StringtokenuserService.userAuth(user);MapString,StringmapnewHashMap();map.put(token,token);returnmap;}然后我们修改business-service1.pom文件引入上面的jwt依赖2.创建JwtUtil和上诉一样3.修改AuthenticationServerFacade的checkPassword方法获取返回tokenpublicStringcheckPassword(Stringusername,Stringpassword){StringurlbaseUrl/user/auth;UserusernewUser();user.setUsername(username);user.setPassword(password);HttpEntityUserrequestnewHttpEntity(user);ResponseEntityMapString,StringresponserestTemplate.exchange(url,HttpMethod.POST,request,newParameterizedTypeReferenceMapString,String(){});returnresponse.getBody().get(token);}4.创建JwtAuthenticationFilter来验证jwtComponentpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{AutowiredprivateJwtUtiljwtUtil;OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{Stringheaderrequest.getHeader(Authorization);if(header!nullheader.startsWith(Bearer )){Stringtokenheader.substring(7);if(jwtUtil.validateToken(token)){StringusernamejwtUtil.getUsernameFromToken(token);StringuseridjwtUtil.getUserIdFromToken(token);UsernamePasswordAuthenticationTokenauthenticationnewUsernamePasswordAuthenticationToken(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)publicResponseEntityMapString,Stringlogin(RequestBodyMapString,Stringcredentials){Stringusernamecredentials.get(username);Stringpasswordcredentials.get(password);// 内部调用 Security-Service 获取 JWT TokenStringtokenauthenticationServerFacade.checkPassword(username,password);MapString,StringresponsenewHashMap();response.put(token,token);returnResponseEntity.ok(response);}}6.修改 SecurityConfig类的 configure(HttpSecurity http)方法引入jwt过滤器并放行/loginOverrideprotectedvoidconfigure(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();JSONObjectresultnewJSONObject();result.put(username,username);result.put(userId,userId);result.put(message,获取用户信息成功);returnresult;}}现在我们用postman测试一下这个接口注意Authorization中要选择 BearerToken并传入刚才生成的token值看结果已经能把jwt中的userId和username解析出来了如果要是想更完善一些可以在认证服务中 创建返回用户信息接口/user/infoRestControllerpublicclassAuthController{AutowiredprivateUserServiceuserService;//基于用户名和密码的认证返回tokenPostMapping(/user/auth)publicMapString,StringuserAuth(RequestBodyUseruser){StringtokenuserService.userAuth(user);MapString,StringmapnewHashMap();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();UseruserauthenticationServerFacade.getUserInfo(username);returnuser;}在AuthenticationServerFacade类中添加对应方法/** * 调用认证中心获取用户详细信息 */publicUsergetUserInfo(Stringusername){StringurlbaseUrl/user/info?usernameusername;ResponseEntityUserresponserestTemplate.getForEntity(url,User.class);returnresponse.getBody();}此时我们用postman在测试一下可以看到 返回的user用户信息已经和 上两集的 数据信息完全对应上了至此jwt已经引入完毕
security第十六集 引入JWT
JWTJSON Web Token 是一种紧凑的、自包含的、用于在各方之间安全传输信息的 JSON 对象格式。它通常用于身份认证和信息交换。一个 JWT 由三部分组成用 . 连接eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c────────── HEADER ────────── ─────────── PAYLOAD ──────────── ──────── SIGNATURE ────────Header头部{alg:HS256,// 签名算法typ:JWT// Token 类型}Payload载荷/数据体{// 标准字段Registered Claimsiss:auth-server,// 签发者sub:1234567890,// 主题通常是用户IDaud:order-service,// 受众exp:1735689600,// 过期时间iat:1735686000,// 签发时间// 自定义字段Public/Private ClaimsuserId:user-001,username:zhangsan,role:ADMIN,permissions:[read,write]}Signature签名javaHMACSHA256(base64UrlEncode(header).base64UrlEncode(payload),secret)JWT工作原理简单了解 jwt 之后 我们根据上两集的多因素认证的 认证服务和客户端进行 一下 改造首先修改一下认证服务1.加入jwt所需maven依赖!--JWT依赖--dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-api/artifactIdversion0.11.5/version/dependencydependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-impl/artifactIdversion0.11.5/versionscoperuntime/scope/dependencydependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt-jackson/artifactIdversion0.11.5/versionscoperuntime/scope/dependency2.创建jwtUtilComponentpublicclassJwtUtil{//这个需自己定义我用的 HS256所以密钥至少要 32 个字符privatestaticfinalStringSECRET_KEYa1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6;privatestaticfinallongEXPIRATION_TIME24*60*60*1000;// 24小时privateSecretKeygetSigningKey(){returnKeys.hmacShaKeyFor(SECRET_KEY.getBytes());}/** * 生成 JWT Token */publicStringgenerateToken(Stringusername,StringuserId){MapString,ObjectclaimsnewHashMap();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){ClaimsclaimsJwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.getSubject();}/** * 从 Token 中获取用户ID */publicStringgetUserIdFromToken(Stringtoken){ClaimsclaimsJwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();returnclaims.get(userId,String.class);}}3.修改UserService中的userAuth 方法返回jwt//验证密码是否匹配if(passwordEncoder.matches(user.getPassword(),u.getPassword())){//密码匹配后生成 JWT TokenStringtokenjwtUtil.generateToken(u.getUsername(),u.getId());returntoken;}else{thrownewBadCredentialsException(Bad credentials);}4.修改AuthController的/user/auth方法返回tokenPostMapping(/user/auth)publicMapString,StringuserAuth(RequestBodyUseruser){StringtokenuserService.userAuth(user);MapString,StringmapnewHashMap();map.put(token,token);returnmap;}然后我们修改business-service1.pom文件引入上面的jwt依赖2.创建JwtUtil和上诉一样3.修改AuthenticationServerFacade的checkPassword方法获取返回tokenpublicStringcheckPassword(Stringusername,Stringpassword){StringurlbaseUrl/user/auth;UserusernewUser();user.setUsername(username);user.setPassword(password);HttpEntityUserrequestnewHttpEntity(user);ResponseEntityMapString,StringresponserestTemplate.exchange(url,HttpMethod.POST,request,newParameterizedTypeReferenceMapString,String(){});returnresponse.getBody().get(token);}4.创建JwtAuthenticationFilter来验证jwtComponentpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{AutowiredprivateJwtUtiljwtUtil;OverrideprotectedvoiddoFilterInternal(HttpServletRequestrequest,HttpServletResponseresponse,FilterChainfilterChain)throwsServletException,IOException{Stringheaderrequest.getHeader(Authorization);if(header!nullheader.startsWith(Bearer )){Stringtokenheader.substring(7);if(jwtUtil.validateToken(token)){StringusernamejwtUtil.getUsernameFromToken(token);StringuseridjwtUtil.getUserIdFromToken(token);UsernamePasswordAuthenticationTokenauthenticationnewUsernamePasswordAuthenticationToken(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)publicResponseEntityMapString,Stringlogin(RequestBodyMapString,Stringcredentials){Stringusernamecredentials.get(username);Stringpasswordcredentials.get(password);// 内部调用 Security-Service 获取 JWT TokenStringtokenauthenticationServerFacade.checkPassword(username,password);MapString,StringresponsenewHashMap();response.put(token,token);returnResponseEntity.ok(response);}}6.修改 SecurityConfig类的 configure(HttpSecurity http)方法引入jwt过滤器并放行/loginOverrideprotectedvoidconfigure(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();JSONObjectresultnewJSONObject();result.put(username,username);result.put(userId,userId);result.put(message,获取用户信息成功);returnresult;}}现在我们用postman测试一下这个接口注意Authorization中要选择 BearerToken并传入刚才生成的token值看结果已经能把jwt中的userId和username解析出来了如果要是想更完善一些可以在认证服务中 创建返回用户信息接口/user/infoRestControllerpublicclassAuthController{AutowiredprivateUserServiceuserService;//基于用户名和密码的认证返回tokenPostMapping(/user/auth)publicMapString,StringuserAuth(RequestBodyUseruser){StringtokenuserService.userAuth(user);MapString,StringmapnewHashMap();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();UseruserauthenticationServerFacade.getUserInfo(username);returnuser;}在AuthenticationServerFacade类中添加对应方法/** * 调用认证中心获取用户详细信息 */publicUsergetUserInfo(Stringusername){StringurlbaseUrl/user/info?usernameusername;ResponseEntityUserresponserestTemplate.getForEntity(url,User.class);returnresponse.getBody();}此时我们用postman在测试一下可以看到 返回的user用户信息已经和 上两集的 数据信息完全对应上了至此jwt已经引入完毕