@@ -826,51 +826,301 @@ public class JWTClient {
826826 System . out. println(" payload:" + claims. getIssuedAt());
827827 System . out. println(" payload:" + claims. getExpiration());
828828 System . out. println(" payload:" + claims. getAudience());
829-
830829 System . out. println(" signature:" + jws. getSignature());
830+ System . out. println(jws. getSignature(). equals(token. split(" \\ ." )[2 ]));
831+ }
832+
833+ public static void main (String [] args ) {
834+ visitServer();
835+ }
836+
837+ }
831838
839+ ```
832840
841+ Spring Boot整合jwt
833842
834- System . out. println(jws. getSignature(). equals(token. split(" \\ ." )[2 ]));
843+ 为了让所有的功能模块 都能拥有 分布式校验功能(jwt),需要将这个校验功能 放到通用模块中。micro_commom:
844+
845+ 1.引入jwt依赖
846+
847+ ``` xml
848+ <dependency >
849+ <groupId >io.jsonwebtoken</groupId >
850+ <artifactId >jjwt</artifactId >
851+ <version >0.9.1</version >
852+ </dependency >
853+
854+ <dependency >
855+ <groupId >org.springframework.boot</groupId >
856+ <artifactId >spring-boot-configuration-processor</artifactId >
857+ <optional >true</optional >
858+ </dependency >
859+ ```
860+
861+ 2.配置
862+
863+ ``` xml
864+ 省略
865+ ```
866+
867+
868+
869+ 3.编码
870+
871+ ``` java
872+ package util ;
873+
874+ import io.jsonwebtoken.Claims ;
875+ import io.jsonwebtoken.Jwts ;
876+ import io.jsonwebtoken.SignatureAlgorithm ;
877+ import org.springframework.boot.context.properties.ConfigurationProperties ;
878+
879+ import java.util.Date ;
835880
881+ /*
882+ * Created by 颜群
883+ */
884+ @ConfigurationProperties (" jwt.config" )
885+ public class JwtUtil {
886+ private String key ;
887+ private long ttl ;
836888
889+ public String getKey () {
890+ return key;
891+ }
837892
893+ public void setKey (String key ) {
894+ this . key = key;
895+ }
838896
897+ public long getTtl () {
898+ return ttl;
839899 }
840900
841- public static void main (String [] args ) {
842- visitServer();
901+ public void setTtl (long ttl ) {
902+ this . ttl = ttl;
903+ }
904+
905+ // 服务端生成jwt-token的方法 (JWTServer)
906+ public String createJWT (String id , String subject ,String roles ){
907+ Date now = new Date () ;
908+ long nowMillis = System . currentTimeMillis() ;
909+
910+ return Jwts . builder(). setId(id)
911+ .setSubject(subject)
912+ .setIssuedAt( now )
913+ .setExpiration( new Date ( nowMillis + ttl ) )
914+ .signWith( SignatureAlgorithm . HS256 , key )
915+ .claim(" roles" ,roles). compact() ;// token
843916 }
844917
918+
919+ // 客户单发送jwt-token时进行的校验方法 (JWTClient)
920+ public Claims parseJWT (String token ){
921+ return Jwts . parser(). setSigningKey(key). parseClaimsJws(token). getBody() ;
922+ }
845923}
846924
925+
847926```
848927
849928
850929
930+ 微服务的校验逻辑:第一登录时 获取token;以后再操作时,需要拿着token去访问。
851931
932+ ![ 1594024091872] ( 微服务.assets/1594024091872.png )
852933
853934
854935
855936
856937
938+ 登录工程:
857939
940+ micro_people
858941
942+ PeopleController.java
859943
944+ ``` java
945+ // 用户名:username
946+ // 密码:password
947+ @PostMapping (value = " /login" )
948+ public Message login(@RequestBody Map<String ,String > login){
949+ // controller -sercice -dao 模拟操作
950+ String uname = login. get(" username" ) ;
951+ String upwd = login. get(" password" ) ;
952+ // findUserByUsernameAndPassword
953+ // zs/abc -> 返回这个人的全部信息 User(zs/abc,id(1001).....)
954+
955+ System . out. println(" 1111" );
956+ // 假设数据库中的zs/abc (并且:假设这个人的id1001,这个人是管理员权限admin )
957+ if (uname. equals(" zs" ) && upwd. equals(" abc" )){
958+ // 登录成功,服务端生成token
959+ // String id, String subject,String roles
960+ String token = jwtUtil. createJWT(" 1001" , " zs" , " admin" );
961+
962+ Map map = new HashMap () ;
963+ map. put(" token" ,token) ;
964+ map. put(" username" ,uname) ;
965+
966+ System . out. println(" 登录成功" );
967+ // 登录成功:返回token\用户名
968+ return new Message (true ,StatusCode . OK ,map) ;
969+ }else {
970+ System . out. println(" 登录失败" );
971+ return new Message (false ,StatusCode . ERROR ) ;
972+ }
860973
861974
975+ }
976+ ```
977+
978+ xml
979+
980+ ```
981+ jwt: config:
982+ key: yanqun
983+ ttl: 1800
984+ ```
985+
986+ ![ 1594027935455] ( 微服务.assets/1594027935455.png )
987+
988+
989+
990+ sb方式的解析:
991+
992+ 客户端校验token - 服务端token
993+
994+ 客户端如何 携带 token去校验? 自定义请求头 Header
862995
996+ key-value
863997
998+ key: authentication
864999
1000+ value: yanqun-token
8651001
1002+ 在需要校验的操作上,增加jwt解析:
1003+
1004+ ``` java
1005+
1006+ @DeleteMapping (" deleteById/{id}" )
1007+ public Message deleteById( @PathVariable (" id" ) Integer id){
1008+
1009+ // 删除之前 先通过jwt进行权限校验
1010+ /*
1011+ 已经存在生成jwt
1012+
1013+ 现在:解析jwt
1014+ 1.客户端将jwt 传递给服务端 (postman模拟)
1015+ 2.解析jwt
1016+ */
1017+ // 假设此时,客户端postman已经将jwt(token)传递过来了
1018+ String authentication = request. getHeader(" authentication" );
1019+ if (authentication == null ) {// 请求时(postman)时,并没有携带jwt
1020+ return new Message (false ,StatusCode . ERROR ," 权限不足!" ) ;
1021+ }
1022+
1023+ String token = authentication. substring(7 );// 从yanqun-token截取出token
1024+ Claims claims = jwtUtil. parseJWT(token);
1025+ String roles = (String )claims. get(" roles" );
1026+ System . out. println(roles+ " roles" );
1027+ if (! roles. equals(" admin" )){
1028+ return new Message (false ,StatusCode . ERROR ," 权限不足!" ) ;
1029+ }
1030+
1031+
1032+ boolean result = cityService. deleteById(id) ;
1033+ return new Message (true , StatusCode . OK , result );
1034+ }
1035+ ```
1036+
1037+ ![ 1595211856571] ( 微服务.assets/1595211856571.png )
8661038
8671039
8681040
1041+ 为了减少代码的冗余量,建议 将解析操作 放到拦截器中。一个微服务,一个拦截器。
8691042
1043+ 定义拦截器
8701044
1045+ ``` java
1046+ package com.yanqun.micro_city.interceptor ;
1047+
1048+ import io.jsonwebtoken.Claims ;
1049+ import org.springframework.beans.factory.annotation.Autowired ;
1050+ import org.springframework.stereotype.Component ;
1051+ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter ;
1052+ import util.JwtUtil ;
8711053
1054+ import javax.servlet.http.HttpServletRequest ;
1055+ import javax.servlet.http.HttpServletResponse ;
8721056
1057+ /*
1058+ * Created by 颜群
1059+ */
1060+ @Component
1061+ public class JwtInterceptor extends HandlerInterceptorAdapter {
8731062
8741063
1064+ // 请求 ->拦截器(pre) -> 增删改
1065+ @Autowired
1066+ private JwtUtil jwtUtil ;
8751067
1068+ @Autowired
1069+ private HttpServletRequest request ;
1070+
1071+
1072+
1073+ @Override
1074+ public boolean preHandle (HttpServletRequest request , HttpServletResponse response , Object handler ) throws Exception {
1075+ System . out. println(" jwt拦截器..." );
1076+ String authentication = request. getHeader(" authentication" );
1077+ if (authentication != null && authentication. startsWith(" yanqun-" )) {
1078+ String token = authentication. substring(7 );
1079+ Claims claims = jwtUtil. parseJWT(token);
1080+ String roles = (String )claims. get(" roles" );
1081+ if (roles. equals(" admin" )){
1082+ request. setAttribute(" claims" ,claims);
1083+ }
1084+ }
1085+ return true ;
1086+ }
1087+ }
1088+
1089+ ```
1090+
1091+ 配置拦截器规则
1092+
1093+ ``` java
1094+ @Configuration
1095+ public class JwtConfig extends WebMvcConfigurationSupport {
1096+
1097+ @Autowired
1098+ private JwtInterceptor jwtInterceptor;
1099+
1100+ @Override
1101+ protected void addInterceptors (InterceptorRegistry registry ) {
1102+ registry. addInterceptor(jwtInterceptor)
1103+ .addPathPatterns(" /**" )
1104+ .excludePathPatterns(" /**/login" ); // 假设city项目中的登录映射叫 login,则排除登录
1105+ }
1106+ }
1107+
1108+ ```
1109+
1110+ 使用拦截器
1111+
1112+ ``` java
1113+ @DeleteMapping (" deleteById/{id}" )
1114+ public Message deleteById( @PathVariable (" id" ) Integer id){
1115+ // 如果拦截器 判断权限足够,则会在request中 放入一个claims参数;否则,没有claims参数
1116+ Claims claims = (Claims )request. getAttribute(" claims" );
1117+ if (claims == null ){
1118+ return new Message (false ,StatusCode . ERROR ," 权限不足" ) ;
1119+ }
1120+
1121+ boolean result = cityService. deleteById(id) ;
1122+ return new Message (true , StatusCode . OK , result );
1123+ }
1124+
1125+ ```
8761126
0 commit comments