有关网站空间正确的说法是劳动合同模板免费
有关网站空间正确的说法是,劳动合同模板免费,网站鼠标特效,百度企业邮箱注册文章目录JWT工具模块Token认证微服务JWT授权监测网关认证过滤消费端获取JWTJWT工具模块
如果要想在项目之中去使用JWT技术#xff0c;那么就必须结合到已有的模块之中,最佳的做法就是将JWT的相关的处理 操作做为一个自动的starter组件进行接入
1、【microcloud项目】既然要开…
文章目录JWT工具模块Token认证微服务JWT授权监测网关认证过滤消费端获取JWTJWT工具模块
如果要想在项目之中去使用JWT技术那么就必须结合到已有的模块之中,最佳的做法就是将JWT的相关的处理 操作做为一个自动的starter组件进行接入
1、【microcloud项目】既然要开发一个starter组件最佳的做法就是开发一个新的模块模块名称:“yootk-starter.jwt ”
2、【microcloud 项目】需要为“yootk-starter-jwt”模块配置所需要的依赖库这些依赖库包括
implementation group: org.springframework.boot, name: spring-boot-configuration-processor, version: 2.5.5
compileOnly group: javax.servlet, name: javax.servlet-api, version: 4.0.1
implementation group: commons-codec, name: commons-codec, version: 1.15
implementation group: io.jsonwebtoken, name: jjwt, version: 0.9.1
implementation group: javax.xml.bind, name: jaxb-api, version: 2.3.1
implementation group: com.sun.xml.bind, name: jaxb-impl, version: 2.3.0
implementation group: com.sun.xml.bind, name: jaxb-core, version: 2.3.03、【microcloud项目】既然已经确定了所需要的项目依赖库随后就可以修改“dependencies.gradle”配置文件定义所依赖模块的配置。
ext.versions [ // 定义全部的依赖库版本号servlet : 4.0.1, // Servlet的依赖库commonsCodec : 1.15, // codec依赖库jjwt : 0.9.1, // jwt依赖库jaxb : 2.3.0, // JAXB依赖库 JDK11需要加的
]
ext.libraries [// 以下的配置为JWT的服务整合servlet-api : javax.servlet:javax.servlet-api:${versions.servlet},commons-codec : commons-codec:commons-codec:${versions.commonsCodec},jjwt : io.jsonwebtoken:jjwt:${versions.jjwt},jaxb-api : javax.xml.bind:jaxb-api:${versions.jaxb},jaxb-impl : com.sun.xml.bind:jaxb-impl:${versions.jaxb},jaxb-core : com.sun.xml.bind:jaxb-core:${versions.jaxb},
]4、【microcloud项目】修改build.gradle配置文件添加相关的依赖
project(:yootk-starter-jwt) { // JWT的实现组件dependencies {annotationProcessor(org.springframework.boot:spring-boot-configuration-processor)implementation(libraries.servlet-api)implementation(libraries.commons-codec)// 以下的组件会被其他的模块继续引用所以必须将其的编译范围配置为compilecompile(libraries.jjwt)compile(libraries.jaxb-api)compile(libraries.jaxb-impl)compile(libraries.jaxb-core)}
}5、【yootk-starter-jwt子模块】由于该模块最终需要进行编译处理所以此时要修改build.gradle配置文件进行任务配置。
jar { enabled true} // 允许打包为jar文件
bootJar { enabled false } // 不允许打包为Boot执行文件
javadocJar { enabled false } // 不需要打包为jar文件
javadocTask { enabled false } // 不需要打包为doc文件6、【yootk-starter-jwt子模块】为了便于用户的信息的相应创建一个JWT响应代码枚举类。
package com.yootk.jwt.code;import javax.servlet.http.HttpServletResponse;public enum JWTResponseCode { // 定义为一个枚举类SUCCESS_CODE(HttpServletResponse.SC_OK, Token数据正确服务正常访问),TOKEN_TIMEOUT_CODE(HttpServletResponse.SC_BAD_REQUEST, Token信息已经失效需要重新申请),NO_AUTH_CODE(HttpServletResponse.SC_NOT_FOUND, 没有找到匹配的Token信息无法进行服务访问);private int code; // 响应的代码private String message; // 响应信息private JWTResponseCode(int code, String message) {this.code code;this.message message;}public String toString() { // 直接将数据以JSON的形式返回return {\code\: this.code ,\message\: this.message };}
}7、 【yootk-starter-jwt】此时的yootk-starter-jwt模块最终是一个自动装配的组件那么既然是组件就需要通过一个配置类来读取引用该模块时所添加的配置信息那么创建一个JWTConfigProperties 配置类。
package com.yootk.jwt.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;Data // Lombok直接生成的所有代码
ConfigurationProperties(prefix yootk.security.config.jwt) // 配置项的前缀
public class JWTConfigProperties { // JWT配置类private String sign; // 保存签名信息private String issuer; // 证书签发者private String secret; // 加密的密钥private long expire; // 失效时间
}8、【yootk-starter-jwt子模块】创建ITokenService服务处理接口专门实现JWT数据的相关处理。
package com.yootk.jwt.service;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;import javax.crypto.SecretKey;
import java.util.Map;public interface ITokenService { // 创建一个JWT的操作接口public SecretKey generalKey(); // 获取当前JWT数据的加密KEY// 创建Token的数据内容同时要求保存用户的id以及所需要的附加数据public String createToken(String id, MapString, Object subject);public JwsClaims parseToken(String token) throws JwtException; // 解析Token数据public boolean verifyToken(String token); // 验证Token有效性public String refreshToken(String token); // 刷新Token内容
}9.【yootk-starter-jwt子模块】创建TokenServicelmpl实现子类很多的数据需要通过JSON实现传递。
package com.yootk.jwt.service.impl;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yootk.jwt.config.JWTConfigProperties;
import com.yootk.jwt.service.ITokenService;
import io.jsonwebtoken.*;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// 此时的组件中的代码需要被其他的模块去引用所以未必会与扫描包相同
public class TokenServiceImpl implements ITokenService {Autowired // SpringBoot容器启动时会自动提供Jackson 实例private ObjectMapper objectMapper; // Jackson的数据处理类对象Autowiredprivate JWTConfigProperties jwtConfigProperties; // 获取JWT的相关配置属性Value(${spring.application.name}) // 通过SpEL进行配置注入private String applicationName; // 应用名称private SignatureAlgorithm signatureAlgorithm SignatureAlgorithm.HS256; // 签名算法Overridepublic SecretKey generalKey() {byte [] encodeKey Base64.decodeBase64(Base64.encodeBase64(this.jwtConfigProperties.getSecret().getBytes()));SecretKey key new SecretKeySpec(encodeKey, 0, encodeKey.length, AES); // 获取加密KEYreturn key;}Overridepublic String createToken(String id, MapString, Object subject) {// 使用JWT数据结构进行开发目的之一就是不需要进行JWT数据的分布式存储所以所谓的缓存组件、数据库都用不到// 所有的Token都存在有保存时效的问题所以就需要通过当前时间来进行计算Date nowDate new Date(); // 获取当前的日期时间Date expireDate new Date(nowDate.getTime() this.jwtConfigProperties.getExpire() * 1000); // 证书过期时间MapString, Object cliams new HashMap(); // 保存所有附加数据cliams.put(site, www.yootk.com); // 视频下载地址顶部有一个下载资源cliams.put(msg, 世界上爆可爱的老师 —— 爆可爱的小李老师); // 随便添加内容cliams.put(nice, Good Good Good);MapString, Object headers new HashMap(); // 保存头信息headers.put(author, 李兴华); // 作者也可以通过配置处理// 后续由于很多的模块都会引用此组件所以为了后续的安全最佳的做法就是设置一个模块名称的信息headers.put(module, this.applicationName);JwtBuilder builder null;try {builder Jwts.builder() // 进行JWTBuilder对象实例化.setClaims(cliams) // 保存附加的数据内容.setHeader(headers) // 保存头信息.setId(id)// 保存ID信息.setIssuedAt(nowDate) // 签发时间.setIssuer(this.jwtConfigProperties.getIssuer()) // 设置签发者.setSubject(this.objectMapper.writeValueAsString(subject)) // 所要传递的数据转为JSON.signWith(this.signatureAlgorithm, this.generalKey()) // 获取签名算法.setExpiration(expireDate); // 配置失效时间} catch (JsonProcessingException e) {e.printStackTrace();}return builder.compact(); // 创建Token}Overridepublic JwsClaims parseToken(String token) throws JwtException {if (this.verifyToken(token)) { // 只有正确的时候再进行Token解析JwsClaims claims Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token);return claims;}return null; // 解析失败返回null}Overridepublic boolean verifyToken(String token) {try {Jwts.parser().setSigningKey(this.generalKey()).parseClaimsJws(token).getBody();return true; // 没有异常就返回true} catch (Exception e) {}return false;}Overridepublic String refreshToken(String token) {if (this.verifyToken(token)) {JwsClaims jws this.parseToken(token); // 解析Token数据return this.createToken(jws.getBody().getId(), this.objectMapper.readValue(jws.getBody().getSubject(), Map.class));}return null;}
}10、【yootk-starter-jwt子模块】定义一个加密的属性配置
package com.yootk.jwt.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;Data
ConfigurationProperties(prefix yootk.security.config.password.encrypt) // 配置前缀
public class EncryptConfigProperties { // 加密配置属性private Integer repeat; // 定义重复的次数private String salt; // 加密的盐值
}
11、【yootk-starter-jwt子模块】既然所有的用户的信息都要保存在数据表里面那么就需要进行密码的加密处理。
package com.yootk.jwt.service;public interface IEncryptService { // 密码加密public String getEncryptPassword(String password); // 得到一个加密后的密码
}12、【yootk-starter-jwt子模块】定义具体的实现子类
package com.yootk.jwt.service.impl;import com.yootk.jwt.config.EncryptConfigProperties;
import com.yootk.jwt.service.IEncryptService;
import org.springframework.beans.factory.annotation.Autowired;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;public class EncryptServiceImpl implements IEncryptService {Autowiredprivate EncryptConfigProperties encryptConfigProperties; // 属性配置private static MessageDigest MD5_DIGEST; // MD5加密处理private static final Base64.Encoder BASE64_ENCODER Base64.getEncoder(); // 加密器static { // 初始化操作try {MD5_DIGEST MessageDigest.getInstance(MD5);} catch (NoSuchAlgorithmException e) {e.printStackTrace();}}Overridepublic String getEncryptPassword(String password) {String saltPassword { this.encryptConfigProperties.getSalt() } password;for (int x 0 ; x this.encryptConfigProperties.getRepeat(); x ) {saltPassword BASE64_ENCODER.encodeToString(MD5_DIGEST.digest(saltPassword.getBytes()));}return saltPassword;}
}13、【yootk-starter-jwt子模块】创建JWT自动配置类
package com.yootk.jwt.autoconfig;import com.yootk.jwt.config.EncryptConfigProperties;
import com.yootk.jwt.config.JWTConfigProperties;
import com.yootk.jwt.service.IEncryptService;
import com.yootk.jwt.service.ITokenService;
import com.yootk.jwt.service.impl.EncryptServiceImpl;
import com.yootk.jwt.service.impl.TokenServiceImpl;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
EnableConfigurationProperties({JWTConfigProperties.class, EncryptConfigProperties.class}) // 配置注入属性
public class JWTAutoConfiguration {Bean(tokenService)public ITokenService getTokenServiceBean() {return new TokenServiceImpl();}Bean(encryptService)public IEncryptService getEncryptServiceBean() {return new EncryptServiceImpl();}
}
14、【yootk-starter-jwt子模块】在“src/main/resources”目录之中创建“META-INF/spring.factories”配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration com.yootk.jwt.autoconfig.JWTAutoConfiguration15、【yootk-starter-jwt子模块】模块开发完成之后来进行编译: gradle build
16、【yootk-starter-jwt子模块】既然已经成功的实现了模块的编译处理随后就需要进行一些环境上的测试创建SpringBoot的配置文件: application.yml
yootk:security:config:jwt:sign: muyanissuer: Muyansecret: yootkexpire: 100 # 单位秒password:encrypt:repeat: 5salt: yootkspring:application:name: JWT-TEST 测试JWT工具模块
17、【yootk-starter-jwt子模块】创建一个程序启动的主类主要是进行测试用的
package com.yootk.jwt;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class StartJWTConfiguration {public static void main(String[] args) {SpringApplication.run(StartJWTConfiguration.class, args);}
}18、【yootk-starter-jwt子模块】编写测试程序进行TokenService测试
package com.yootk.test;import com.yootk.jwt.StartJWTConfiguration;
import com.yootk.jwt.service.ITokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwsHeader;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;ExtendWith(SpringExtension.class)
WebAppConfiguration
SpringBootTest(classes StartJWTConfiguration.class) // 随便写的测试类
public class TestTokenService { // 代码测试Autowiredprivate ITokenService tokenService;private String jwt eyJhdXRob3IiOiLmnY7lhbTljY4iLCJtb2R1bGUiOiJKV1QtVEVTVCIsImFsZyI6IkhTMjU2In0.eyJtc2ciOiLkuJbnlYzkuIrniIblj6_niLHnmoTogIHluIgg4oCU4oCUIOeIhuWPr-eIseeahOWwj-adjuiAgeW4iCIsInN1YiI6IntcInJpZHNcIjpcIlVTRVI7QURNSU47REVQVDtFTVA7Uk9MRVwiLFwibmFtZVwiOlwi5rKQ6KiA56eR5oqAIOKAlOKAlCDmnY7lhbTljY5cIixcIm1pZFwiOlwibXV5YW5cIn0iLCJzaXRlIjoid3d3Lnlvb3RrLmNvbSIsImlzcyI6Ik11eWFuWW9vdGsiLCJleHAiOjE2MzM2NzE3NjcsImlhdCI6MTYzMzU3MTc2NywibmljZSI6Ikdvb2QgR29vZCBHb29kIiwianRpIjoieW9vdGstMDgwNGI3NDQtNTBjZC00NjI2LTgzNmEtNjA1MmFiZWMyYzQ4In0.O71QGGPtWYwL7Tyhx8iOLQFAWc1DmVlAS4i0N99OJJk; // 测试解析使用的Testpublic void testCreateToken() {MapString, Object map new HashMap(); // 测试生成map.put(mid, muyan);map.put(name, 沐言科技 —— 李兴华);map.put(rids, USER;ADMIN;DEPT;EMP;ROLE); // 用户角色信息String id yootk- UUID.randomUUID(); // 随意生成一个JWT-ID数据System.out.println(this.tokenService.createToken(id, map));}Testpublic void testParseToken() { // 解析Token数据内容JwsClaims jws this.tokenService.parseToken(jwt);System.out.println(JWT签名数据 jws.getSignature()); // 获取签名数据JwsHeader headers jws.getHeader(); // 获取头信息headers.forEach((headerName, headerValue) - {System.out.println(【JWT头信息】 headerName headerValue);});Claims claims jws.getBody();claims.forEach((bodyName, bodyValue) - {System.out.println(【JWT数据】 bodyName bodyValue);});} Testpublic void testVerifyJWT() {System.out.println(【JWT数据验证】 this.tokenService.verifyToken(jwt));}Testpublic void testRefreshJWT() {System.out.println(【JWT数据刷新】 this.tokenService.refreshToken(jwt));}
}19、【yootk-starter-jwt子模块】随后进行密码加密的测试
package com.yootk.test;import com.yootk.jwt.StartJWTConfiguration;
import com.yootk.jwt.service.IEncryptService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;ExtendWith(SpringExtension.class)
WebAppConfiguration
SpringBootTest(classes StartJWTConfiguration.class) // 随便写的测试类
public class TestEncryptService {Autowiredprivate IEncryptService encryptService;Testpublic void testCreatePassword() {System.out.println(this.encryptService.getEncryptPassword(hello));}
}
此时已经成功的开发出了一套完整的与JWT相关的应用组件模块使用的时候直接导入依赖库即可应用。
Token认证微服务 虽然此时已经给出了JWT相关的自动装配组件,但是这个组件最终如果要想应用起来还需要提供有一个具体的Token微服务功能就是根据用户的认证的请求来实现相关Token数据的生成了。 由于当前的设计是 通过JWT存储了认证数据以及授权数据在这样的过程里面就需要通过数据库来实现用户的统一管理即:应该提供有用户表、授权表的信息。 1、【microcloud项目】创建“token-server-8201”子模块随后修改build.gradle配置文件为其添加相关的依赖
project(:token-server-8201) { // 部门微服务dependencies {implementation(project(:common-api)) // 导入公共的子模块implementation(project(:yootk-starter-jwt)) // 导入JWT子模块implementation(libraries.mybatis-plus-boot-starter)implementation(libraries.mysql-connector-java)implementation(libraries.druid)implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel)// 以下的依赖库为Nacos注册中心所需要的依赖配置implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery) {exclude group: com.alibaba.nacos, module: nacos-client // 移除旧版本的Nacos依赖}implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config) {exclude group: com.alibaba.nacos, module: nacos-client // 移除旧版本的Nacos依赖}implementation(libraries.nacos-client) // 引入与当前的Nacos匹配的依赖库}
}2、【token-server-8201子模块】根据当前的需要创建数据表
DROP DATABASE IF EXISTS token8201;
CREATE DATABASE token8201 CHARACTER SET UTF8 ;
USE token8201 ;
CREATE TABLE member(mid VARCHAR(50) NOT NULL,name VARCHAR(30),password VARCHAR(32),locked INT,dbname VARCHAR(50),CONSTRAINT pk_mid PRIMARY KEY (mid)
) engineinnodb;
CREATE TABLE role(rid VARCHAR(50) ,title VARCHAR(200) ,dbname VARCHAR(50),CONSTRAINT pk_rid PRIMARY KEY(rid)
) engineinnodb ;
CREATE TABLE action(actid VARCHAR(50) ,title VARCHAR(200) ,rid VARCHAR(50) ,dbname VARCHAR(50),CONSTRAINT pk_actid PRIMARY KEY(actid)
) engineinnodb ;
CREATE TABLE member_role(mid VARCHAR(50) ,rid VARCHAR(50) ,dbname VARCHAR(50)
) engineinnodb ;
-- 1表示活跃、0表示锁定用户密码铭文hello
INSERT INTO member(mid, name, password, locked, dbname) VALUES(admin, 管理员, Wx7vJ71XD3TgJg5uiETnKA, 0, database()) ;
INSERT INTO member(mid, name, password, locked, dbname) VALUES(yootk, 用户, Wx7vJ71XD3TgJg5uiETnKA, 0, database()) ;
INSERT INTO member(mid, name, password, locked, dbname) VALUES(mermaid, 美人鱼, Wx7vJ71XD3TgJg5uiETnKA, 1, database()) ;
-- 定义角色信息
INSERT INTO role(rid, title, dbname) VALUES (member, 用户管理, database()) ;
INSERT INTO role(rid, title, dbname) VALUES (dept, 部门管理, database()) ;
INSERT INTO role(rid, title, dbname) VALUES (emp, 雇员管理, database()) ;
-- 定义权限信息
INSERT INTO action(actid, title, rid, dbname) VALUES (member:add, 创建用户, member, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (member:edit, 编辑用户, member, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (member:delete, 删除用户, member, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (member:list, 用户列表, member, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (dept:add, 创建部门, dept, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (dept:edit, 编辑部门, dept, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (dept:delete, 删除部门, dept, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (dept:list, 部门列表, dept, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (emp:add, 增加雇员, emp, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (emp:edit, 编辑雇员, emp, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (emp:delete, 删除雇员, emp, database()) ;
INSERT INTO action(actid, title, rid, dbname) VALUES (emp:list, 雇员列表, emp, database()) ;
-- 定义用户与角色的关系
INSERT INTO member_role(mid, rid, dbname) VALUES (admin, member, database()) ;
INSERT INTO member_role(mid, rid, dbname) VALUES (admin, dept, database()) ;
INSERT INTO member_role(mid, rid, dbname) VALUES (admin, emp, database()) ;
INSERT INTO member_role(mid, rid, dbname) VALUES (yootk, emp, database()) ;
INSERT INTO member_role(mid, rid, dbname) VALUES (mermaid, dept, database()) ;
COMMIT ;3、【token-server-8201子模块】在项目之中application.yml文件
server: # 服务端配置port: 8201 # 8201端口
mybatis-plus: # MyBatisPlus配置type-aliases-package: com.yootk.provider.vo # 别名配置
spring:application: # 配置应用信息name: token.provider # 是微服务的名称cloud: # Cloud配置sentinel: # 监控配置transport: # 传输配置port: 8719 # Sentinel组件启用之后默认会启动一个8719端口dashboard: sentinel-server:8888 # 控制台地址nacos: # Nacos注册中心配置discovery: # 发现服务weight: 80service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称server-addr: nacos-server:8848 # Nacos服务地址namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间IDgroup: MICROCLOUD_GROUP # 一般建议大写cluster-name: MuyanCluster # 配置集群名称metadata: # 根据自身的需要配置元数据version: 1.0 # 自定义元数据项datasource: # 数据源配置type: com.alibaba.druid.pool.DruidDataSource # 数据源类型driver-class-name: com.mysql.cj.jdbc.Driver # 驱动程序类url: jdbc:mysql://localhost:3306/token8201 # 连接地址username: root # 用户名password: mysqladmin # 连接密码druid: # druid相关配置initial-size: 5 # 初始化连接池大小min-idle: 10 # 最小维持连接池大小max-active: 50 # 最大支持连接池大小max-wait: 60000 # 最大等待时间time-between-eviction-runs-millis: 60000 # 关闭空闲连接间隔min-evictable-idle-time-millis: 30000 # 连接最小生存时间validation-query: SELECT 1 FROM dual # 状态检测test-while-idle: true # 空闲时检测连接是否有效test-on-borrow: false # 申请时检测连接是否有效test-on-return: false # 归还时检测连接是否有效pool-prepared-statements: false # PSCache缓存max-pool-prepared-statement-per-connection-size: 20 # 配置PS缓存filters: stat, wall, slf4j # 开启过滤stat-view-servlet: # 监控界面配置enabled: true # 启用druid监控界面allow: 127.0.0.1 # 访问白名单login-username: muyan # 用户名login-password: yootk # 密码reset-enable: true # 允许重置url-pattern: /druid/* # 访问路径web-stat-filter:enabled: true # 启动URI监控url-pattern: /* # 跟踪全部服务exclusions: *.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/* # 跟踪排除filter:slf4j: # 日志enabled: true # 启用SLF4j监控data-source-log-enabled: true # 启用数据库日志statement-executable-sql-log-enable: true # 执行日志result-set-log-enabled: true # ResultSet日志启用stat: # SQL监控merge-sql: true # 合并统计log-slow-sql: true # 慢执行记录slow-sql-millis: 1 # 慢SQL执行时间wall: # SQL防火墙enabled: true # SQL防火墙config: # 防火墙规则multi-statement-allow: true # 允许执行批量SQLdelete-allow: false # 禁止执行删除语句aop-patterns: com.yootk.provider.action.*,com.yootk.provider.service.*,com.yootk.provider.dao.* # Spring监控
4、【token-server-8201子模块】除了以上的操作部分之外那么剩下的就需要开发者自己去定义与WT有关的配置项。
yootk:security:config:jwt:sign: muyanissuer: MuyanYootksecret: www.yootk.comexpire: 100000 # 单位秒password:encrypt:repeat: 5salt: www.yootk.com5、【token-server-8201子模块】创建Member表的映射转换
package com.yootk.provider.vo;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(member) // 映射表名称
public class Member {TableId // 主键字段private String mid;private String name;private String password;private Integer locked;private String dbname;
}6、【token-server-8201子模块】配置Role角色处理类
package com.yootk.provider.vo;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
Data
TableName(role)
public class Role {TableIdprivate String rid;private String title;private String dbname;
}7、【token-server-8201子模块】定义权限表映射类
package com.yootk.provider.vo;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;Data
TableName(action)
public class Action {TableIdprivate String actid;private String title;private String rid;private String dbname;
}8、【token-server-8201子模块】创建IMemberDAO接口
package com.yootk.provider.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yootk.provider.vo.Member;
import org.apache.ibatis.annotations.Mapper;Mapper
public interface IMemberDAO extends BaseMapperMember {
}
9、【token-server-8201子模块】创建IRoleDAO接口
package com.yootk.provider.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yootk.provider.vo.Role;
import org.apache.ibatis.annotations.Mapper;import java.util.Set;Mapper
public interface IRoleDAO extends BaseMapperRole {public SetString findAllByMember(String mid); // 根据用户名查询角色
}
10、【token-server-8201子模块】创建IActionDAO接口
package com.yootk.provider.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yootk.provider.vo.Action;
import org.apache.ibatis.annotations.Mapper;import java.util.Set;Mapper
public interface IActionDAO extends BaseMapperAction {public SetString findAllByMember(String mid); // 获取权限信息
}11、【token-server-8201子模块】定义“src/main/resources/META-INF/mybatis/mapper/MemberMapper.xml”文件
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.yootk.provider.dao.IMemberDAO
/mapper12、【token-server-8201子模块】创建RoleMapper.xml文件
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.yootk.provider.dao.IRoleDAOselect idfindAllByMember parameterTypestring resultTypestringSELECT rid FROM member_role WHERE mid#{mid}/select
/mapper
13、【token-server-8201子模块】创建ActionMapper.xml文件
?xml version1.0 encodingUTF-8?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.yootk.provider.dao.IActionDAOselect idfindAllByMember parameterTypestring resultTypestringSELECT actid FROM action WHERE rid IN(SELECT rid FROM member_role WHERE mid#{mid})/select
/mapper14、【common-api子模块】因为此时需要牵扯到用户认证信息的传输所以这个时候需要创建一个DTO传输类
package com.yootk.common.dto;import lombok.Data;Data
public class MemberDTO {private String mid;private String password;
}15、【common-api子模块】创建IMemberService业务接口实现认证与授权信息获取
package com.yootk.service;import com.yootk.common.dto.MemberDTO;import java.util.Map;public interface IMemberService {// 用户登录完成之后所有的数据通过Map集合进行返回而后会包含有如下的一些数据内容// 1、key status、value 登录状态true、false// 2、key mid、value 用户名// 3、key name、value 姓名// 4、key resource、value 授权信息// 4-1、key roles、value 用户拥有的全部角色// 4-2、key roles、value 用户拥有的全部的权限public MapString, Object login(MemberDTO memberDTO);
}16、【token-server-8201子模块】定义IMemberService 业务接口的实现类
package com.yootk.provider.service.impl;import com.yootk.common.dto.MemberDTO;
import com.yootk.provider.dao.IActionDAO;
import com.yootk.provider.dao.IMemberDAO;
import com.yootk.provider.dao.IRoleDAO;
import com.yootk.provider.vo.Member;
import com.yootk.service.IMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;Service
public class MemberServiceImpl implements IMemberService {Autowiredprivate IMemberDAO memberDAO;Autowiredprivate IRoleDAO roleDAO;Autowiredprivate IActionDAO actionDAO;Overridepublic MapString, Object login(MemberDTO memberDTO) {MapString, Object result new HashMap();Member member this.memberDAO.selectById(memberDTO.getMid()); // 查询用户数据// 用户信息为空、密码不相等或者用户状态被锁定if (member null || !member.getPassword().equals(memberDTO.getPassword()) || member.getLocked().equals(1)) {result.put(status, false); // 登录失败} else { // 一切正常获取其他信息result.put(status, true); // 登录成功result.put(mid, memberDTO.getMid());result.put(name, member.getName());MapString, Object resource new HashMap();resource.put(roles, this.roleDAO.findAllByMember(memberDTO.getMid()));resource.put(actions, this.actionDAO.findAllByMember(memberDTO.getMid()));result.put(resource, resource);}return result;}
}17、【token-server-8201子模块】编写一个程序启动类
package com.yootk.provider;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;SpringBootApplication
EnableDiscoveryClient
public class StartTokenApplication8201 {public static void main(String[] args) {SpringApplication.run(StartTokenApplication8201.class, args);}
}18、【token-server-8201子模块】编写测试类
package com.yootk.test;import com.yootk.common.dto.MemberDTO;
import com.yootk.jwt.StartJWTConfiguration;
import com.yootk.jwt.service.IEncryptService;
import com.yootk.provider.StartTokenApplication8201;
import com.yootk.service.IMemberService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;ExtendWith(SpringExtension.class)
WebAppConfiguration
SpringBootTest(classes StartTokenApplication8201.class) // 随便写的测试类
public class TestMemberService {Autowiredprivate IMemberService memberService;Autowiredprivate IEncryptService encryptService; // 自动装配模块提供的Testpublic void testLogin() {MemberDTO memberDTO new MemberDTO();memberDTO.setMid(admin);memberDTO.setPassword(this.encryptService.getEncryptPassword(hello));System.out.println(this.memberService.login(memberDTO));}
}19、【token-server-8201子模块】Service接口测试通过之后下面就需要进行Action接口发布
package com.yootk.provider.action;import com.yootk.common.dto.MemberDTO;
import com.yootk.jwt.service.IEncryptService;
import com.yootk.jwt.service.ITokenService;
import com.yootk.service.IMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;RestController
RequestMapping(/token/*)
public class TokenAction {Autowiredprivate IMemberService memberService; // 本模块提供的Autowiredprivate IEncryptService encryptService; // yootk-starter-jwt模块提供的Autowiredprivate ITokenService tokenService; // yootk-starter-jwt模块提供的RequestMapping(create)public Object login(RequestBody MemberDTO memberDTO) {// 对用户传入的密码信息进行加密处理memberDTO.setPassword(this.encryptService.getEncryptPassword(memberDTO.getPassword()));MapString, Object result this.memberService.login(memberDTO); // 登录业务处理if (((Boolean)result.get(status))) { // 登录状态return this.tokenService.createToken(result.get(mid).toString(), (MapString, Object) result.get(resource));}return null;}RequestMapping(parse)public Object parseToken(String token) {return this.tokenService.parseToken(token); // Token解析处理}
}20、【Nacos 控制台】在Nacos 控制台为Token 服务添加配置项 22、【Postman测试】测试Token生成
token-server-8201:8201/token/create?midadminpasswordhello23、【Postman测试】测试Token解析
token-server-8201:8201/token/parse?tokeneyJhdXRob3IiOiLmnY7lhbTljY4iLCJtb2R1bGUiOiJ0b2tlbi5wcm92aWRlciIsImFsZyI6IkhTMjU2In0.eyJtc2ciOiLkuJbnlYzkuIrniIblj6_niLHnmoTogIHluIgg4oCU4oCUIOeIhuWPr-eIseeahOWwj-adjuiAgeW4iCIsInN1YiI6IntcInJvbGVzXCI6W1wibWVtYmVyXCIsXCJlbXBcIixcImRlcHRcIl0sXCJhY3Rpb25zXCI6W1wiZW1wOmxpc3RcIixcImRlcHQ6ZWRpdFwiLFwiZGVwdDpsaXN0XCIsXCJlbXA6ZWRpdFwiLFwibWVtYmVyOmFkZFwiLFwiZGVwdDphZGRcIixcImVtcDphZGRcIixcIm1lbWJlcjplZGl0XCIsXCJkZXB0OmRlbGV0ZVwiLFwibWVtYmVyOmRlbGV0ZVwiLFwibWVtYmVyOmxpc3RcIixcImVtcDpkZWxldGVcIl19Iiwic2l0ZSI6Ind3dy55b290ay5jb20iLCJpc3MiOiJNdXlhbllvb3RrIiwiZXhwIjoxNjMzNjc2MjIwLCJpYXQiOjE2MzM1NzYyMjAsIm5pY2UiOiJHb29kIEdvb2QgR29vZCIsImp0aSI6ImFkbWluIn0.3HA8dqdgi9Lr0Nlzg76CoJiiFcDwK-Vh9nf5facEfRQJWT授权监测 JWT本身不具备有这种所谓的授权检测支持只是我们利用其附加数据的能力实现了这样的授权检测在之前登录成功后就可以通过附加数据保存所有的授权的信息(分为了角色以及权限两种)。 后面肯定是由消费端来获取JWT数据并且依据JWT数据实现微服务资源的调用那么在这样的情况下就可以设置有一个自定义的Annotation(注解)而后在注解里面可以根据需要进行角色或权限的检查注解如果要想在微服务端生效则可以利用拦截器的形式来进行处理。 1、【yootk-starter-jwt子模块】所有与JWT有关的操作实际上都由该模块提供这样在代码之中就可以考虑将角色和权限检查的部分交给该模块来实现定义一个专属的工具类。
package com.yootk.jwt.util;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yootk.jwt.service.ITokenService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.springframework.beans.factory.annotation.Autowired;import javax.servlet.http.HttpServletRequest;
import java.util.*;// 所有的数据最终都是通过JSON的形式设置在JWT附加数据之中的
public class JWTMemberDataService { // 自定义的数据的解析类Autowiredprivate ITokenService tokenService;Autowiredprivate ObjectMapper objectMapper; // 解析JSON数据为Map集合public MapString, String headers(String token) { // 通过JWT解析所有的头信息JwsClaims claimsJws this.tokenService.parseToken(token);MapString, String headers new HashMap(); // 保存所有的头信息的集合claimsJws.getHeader().forEach((key, value) - { // 将JWT头信息转为Mapheaders.put(key.toString(), value.toString()); // 数据以String的方式存储});return headers;}public SetString roles(String token) { // 解析全部的角色数据JwsClaims claimsJws this.tokenService.parseToken(token);try {MapString, ListString map this.objectMapper.readValue(claimsJws.getBody().getSubject(), Map.class);SetString roles new HashSet();roles.addAll(map.get(roles)); // 将获取的全部角色保存在Set集合return roles;} catch (JsonProcessingException e) {e.printStackTrace();}return null;}public SetString actions(String token) { // 解析全部的权限数据JwsClaims claimsJws this.tokenService.parseToken(token);try {MapString, ListString map this.objectMapper.readValue(claimsJws.getBody().getSubject(), Map.class);SetString actions new HashSet();actions.addAll(map.get(actions)); // 将获取的全部角色保存在Set集合return actions;} catch (JsonProcessingException e) {e.printStackTrace();}return null;}public String id(String token) {JwsClaims claimsJws this.tokenService.parseToken(token);return claimsJws.getBody().getId();}public String getToken(HttpServletRequest request, String name) { // Token获取String token request.getParameter(name); // name为参数的名称if (token null || .equals(token)) { // 无法通过参数获取数据token request.getHeader(name); // 通过头信息传递}return token;}
}2、【yootk-starter-jwt子模块】既然最终该工具类要交由外部进行调用所以在自动装配类里面追加一些配置
package com.yootk.jwt.autoconfig;import com.yootk.jwt.config.EncryptConfigProperties;
import com.yootk.jwt.config.JWTConfigProperties;
import com.yootk.jwt.service.IEncryptService;
import com.yootk.jwt.service.ITokenService;
import com.yootk.jwt.service.impl.EncryptServiceImpl;
import com.yootk.jwt.service.impl.TokenServiceImpl;
import com.yootk.jwt.util.JWTMemberDataService;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;Configuration
EnableConfigurationProperties({JWTConfigProperties.class, EncryptConfigProperties.class}) // 配置注入属性
public class JWTAutoConfiguration {Bean(tokenService)public ITokenService getTokenServiceBean() {return new TokenServiceImpl();}Bean(encryptService)public IEncryptService getEncryptServiceBean() {return new EncryptServiceImpl();}Bean(memberDataService)public JWTMemberDataService getMemberDataService() {return new JWTMemberDataService();}
}3、【yootk-starter-jwt子模块】为项目添加一个注解这个注解主要是根据JWT的数据来实现授权检测
package com.yootk.jwt.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;Target({ElementType.METHOD}) // 在方法上使用
Retention(RetentionPolicy.RUNTIME) // 运行时生效
public interface JWTCheckToken {boolean required() default true; // 配置的启用认证排查String role() default ; // 角色检查String action() default ; // 权限检查
}4、【yootk-starter-jwt子模块】对当前的模块进行重新打包: gradle build
打包之前 yootk-starter-jwt子模块的build.gradle 5、【provider-dept-8002子模块】在部门微服务之中引入jwt的组件模块
project(:provider-dept-8002) { // 部门微服务dependencies {implementation(project(:common-api)) // 导入公共的子模块implementation(project(:yootk-starter-jwt)) // 导入JWT子模块implementation(libraries.mybatis-plus-boot-starter)implementation(libraries.mysql-connector-java)implementation(libraries.druid)implementation(libraries.springfox-boot-starter)implementation(org.springframework.boot:spring-boot-starter-security)implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel)// 以下的依赖库为Nacos注册中心所需要的依赖配置implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery) {exclude group: com.alibaba.nacos, module: nacos-client // 移除旧版本的Nacos依赖}implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config) {exclude group: com.alibaba.nacos, module: nacos-client // 移除旧版本的Nacos依赖}implementation(libraries.nacos-client) // 引入与当前的Nacos匹配的依赖库}
}6、【provider-dept-8002子模块】修改DeptAction程序类添加JWT的注解
package com.yootk.provider.action;import com.yootk.common.dto.DeptDTO;
import com.yootk.jwt.annotation.JWTCheckToken;
import com.yootk.service.IDeptService;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;RestController
RequestMapping(/provider/dept/*) // 微服务提供者父路径
Slf4j // 使用一个注解
public class DeptAction {Autowiredprivate IDeptService deptService;ApiOperation(value部门查询, notes 根据部门编号查询部门详细信息)GetMapping(get/{id})JWTCheckToken(roledept) // 进行JWT的角色检查public Object get(PathVariable(id) long id) {this.printRequestHeaders(get);return this.deptService.get(id);}ApiOperation(value部门增加, notes 增加新的部门信息)ApiImplicitParams({ApiImplicitParam(name deptDTO, required true,dataType DeptDTO, value 部门传输对象实例)})PostMapping(add)public Object add(RequestBody DeptDTO deptDTO) { // 后面会修改参数模式为JSONthis.printRequestHeaders(add);return this.deptService.add(deptDTO);}ApiOperation(value部门列表, notes 查询部门的完整信息)GetMapping(list)JWTCheckToken(action dept:list) // 权限检查public Object list() {this.printRequestHeaders(list);return this.deptService.list();}ApiOperation(value部门分页查询, notes 根据指定的数据库参数实现部门数据的分页加载)ApiImplicitParams({ApiImplicitParam(namecp, value 当前所在页, required true, dataType int),ApiImplicitParam(namels, value 每页显示的数据行数, required true, dataType int),ApiImplicitParam(namecol, value 模糊查询列, required true, dataType String),ApiImplicitParam(namekw, value 模糊查询关键字, required true, dataType String)})GetMapping(split)JWTCheckToken // 只要追加了此注解就表示要进行JWT有效性检查public Object split(int cp, int ls, String col, String kw) {this.printRequestHeaders(split);return this.deptService.split(cp, ls, col, kw);}GetMapping(message)public Object message(String message) { // 接收参数log.info(接收到请求参数message {}, message);printRequestHeaders(message);return message;}private void printRequestHeaders(String restName) { // 实现所有请求头信息的输出HttpServletRequest request ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();EnumerationString headerEnums request.getHeaderNames();while (headerEnums.hasMoreElements()) {String headerName headerEnums.nextElement();log.info(【{}】头信息{} {}, restName, headerName, request.getHeader(headerName));}}
}7、【provider-dept-8002子模块】如果要想让当前的注解生效则一定要开发一个专属的JWT的拦截器
package com.yootk.provider.interceptor;import com.yootk.jwt.annotation.JWTCheckToken;
import com.yootk.jwt.code.JWTResponseCode;
import com.yootk.jwt.service.ITokenService;
import com.yootk.jwt.util.JWTMemberDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;public class JWTAuthorizeInterceptor implements HandlerInterceptor {// 此时需要确定有一个Token数据接收的参数名称这个Token可能通过地址重写传递或者是利用头信息传递private static final String TOKEN_NAME yootk-token;Autowired // 区分出角色和权限的信息private JWTMemberDataService memberDataService;Autowired // JWT有效性的检查private ITokenService tokenService;Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {boolean flag true; // 拦截if (!(handler instanceof HandlerMethod)) { // 类型不匹配return flag;}HandlerMethod handlerMethod (HandlerMethod) handler; // 因为需要对Action进行解析处理Method method handlerMethod.getMethod(); // 获取调用的方法对象if (method.isAnnotationPresent(JWTCheckToken.class)) { // 当前的方法上存在有指定注解// 如果发现此时出现了Token的错误则肯定要直接进行响应不会走到Action响应上response.setCharacterEncoding(UTF-8);response.setContentType(application/json;charsetutf-8);JWTCheckToken checkToken method.getAnnotation(JWTCheckToken.class); // 获取配置注解if (checkToken.required()) { // 启用JWT检查// JWT的数据可能来自于参数或者是头信息String token this.memberDataService.getToken(request, TOKEN_NAME);if (!StringUtils.hasLength(token)) { // 没有Token数据flag false;response.getWriter().println(JWTResponseCode.NO_AUTH_CODE); // 直接响应错误代码} else { // 此时的Token存在if (!this.tokenService.verifyToken(token)) { // Token校验失败flag false;response.getWriter().println(JWTResponseCode.TOKEN_TIMEOUT_CODE);} else { // Token没有失败if (!(checkToken.role() null || .equals(checkToken.role()))) { // 需要进行角色检查// 根据Token字符串解析出所有的角色集合而后判断是否存在有指定的角色信息if (this.memberDataService.roles(token).contains(checkToken.role())) {flag true; // 允许访问} else { // 失败访问response.getWriter().println(JWTResponseCode.NO_AUTH_CODE);flag false; // 不允许访问}} else if (!(checkToken.action() null || .equals(checkToken.action()))) {if (this.memberDataService.actions(token).contains(checkToken.action())) {flag true; // 允许访问} else { // 失败访问response.getWriter().println(JWTResponseCode.NO_AUTH_CODE);flag false; // 不允许访问}} else {flag true;}}}}}return flag;}
}8、【provider-dept-8002子模块】拦截器开发完成之后需要进行拦截器的配置类的定义
package com.yootk.provider.config;import com.yootk.provider.interceptor.JWTAuthorizeInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
Configuration
public class JWTInterceptorConfig implements WebMvcConfigurer {Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(this.getDefaultHandlerInterceptor()).addPathPatterns(/**);}Beanpublic HandlerInterceptor getDefaultHandlerInterceptor() {return new JWTAuthorizeInterceptor();}
}9、【provider-dept-8002子模块】如果要想正确的驱动“yootk-starter-jwt”组件那么就需要在application.yml里面添加与JWT组件模块有关的配置项
yootk:security:config:jwt:sign: muyanissuer: MuyanYootksecret: www.yootk.comexpire: 100000 # 单位秒password:encrypt:repeat: 5salt: www.yootk.com10、【Postman工具】现在全部的代码已经改造完成之后下面直接启动Postman进行测试 只有传递了正确的Token数据才可以实现相应的微服务的访问此时就可以通过授权的检测形式来保护你微服务的安全。
网关认证过滤 所有的微服务最终都是通过网关来实现分配的那么网关之中就首先必须对消费端的调用进行JWT认证检查网关除了具备有服务分类治理功能之外实际上还有认证的检查功能它是一个入口。 在很早以前的SpringCloud还是使用OAuth2做单点登录统一管理的时候网关也需要进行一系列的开发配置而后随着版本的更新OAuth2的支持度开始下降于是各种新版本的整合就非常痛苦 如果要想实现这种JWT的检查机制那么只能够在过滤器之中完成处理而在过滤器检查的时候需要注意配置的问题例如:JWT参数的名称、非检查路径例如:“/token/create)。 1、【gateway-9501子模块】首先要追加上JWT模块有关的依赖库修改application.yml配置文件随后还需要添加网关的一些自定义的配置属性。
yootk:security:config:jwt:sign: muyanissuer: MuyanYootksecret: www.yootk.comexpire: 100000 # 单位秒password:encrypt:repeat: 5salt: www.yootk.com
gateway: # 自定义的配置项config:jwt:header-name: yootk-token # 头信息的参数名称skip-auth-urls: # 跳过的检查路径- /token/create2、【gateway-9501子模块】定义一个与当前配置相关的程序类实现配置项的读取
package com.yootk.gateway.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;import java.util.List;Component
Data
ConfigurationProperties(prefix gateway.config.jwt) // 定义配置头
public class GatewayJWTConfigProperties { // 网关的配置项private ListString skipAuthUrls; // 配置的跳过路径private String headerName; // 头信息名称
}3、【microcloud 项目】修改build.gradle为gateway-9501子模块添加依赖
project(:gateway-9501) { // 网关模块dependencies {implementation(com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery) {exclude group: com.alibaba.nacos, module: nacos-client // 移除旧版本的Nacos依赖}implementation(project(:yootk-starter-jwt)) // 导入JWT子模块implementation(libraries.nacos-client) // 引入与当前的Nacos匹配的依赖库implementation(org.springframework.cloud:spring-cloud-starter-gateway) // 网关依赖implementation(org.springframework.boot:spring-boot-starter-actuator) // Actuator依赖库implementation(org.springframework.cloud:spring-cloud-starter-loadbalancer)implementation(libraries.caffeine)implementation(libraries.micrometer-registry-prometheus)implementation(libraries.micrometer-core)}
}4、【gateway-9501子模块】定义全局过滤器
package com.yootk.gateway.filter.global;import com.alibaba.nacos.api.utils.StringUtils;
import com.yootk.gateway.config.GatewayJWTConfigProperties;
import com.yootk.jwt.code.JWTResponseCode;
import com.yootk.jwt.service.ITokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;Component
Slf4j
public class JWTTokenCheckFilter implements GlobalFilter { // 全局过滤器Autowiredprivate GatewayJWTConfigProperties jwtConfig; // JWT的相关配置属性Autowiredprivate ITokenService tokenService; // 进行Token处理Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {String url exchange.getRequest().getURI().getPath(); // 获取路径if (this.jwtConfig.getSkipAuthUrls() ! null this.jwtConfig.getSkipAuthUrls().contains(url)) {return chain.filter(exchange); // 向下继续执行其他的后续操作}// 网关将通过头信息获取到JWT的数据内容网关技术通过WebFlux技术开发的String token exchange.getRequest().getHeaders().get(this.jwtConfig.getHeaderName()).get(0);log.info(网关Token检查Token {}, token); // 日志输出// 如果假设Token有错误了那么网关是需要直接进行响应的请求肯定不会发送给目标的微服务ServerHttpResponse response exchange.getResponse();if (StringUtils.isBlank(token)) { // Token数据为空DataBuffer buffer response.bufferFactory().wrap(JWTResponseCode.NO_AUTH_CODE.toString().getBytes(StandardCharsets.UTF_8));return response.writeWith(Flux.just(buffer)); // 异步响应错误} else { // Token数据不为空if (this.tokenService.verifyToken(token)) { // 校验成功return chain.filter(exchange);} else {DataBuffer buffer response.bufferFactory().wrap(JWTResponseCode.TOKEN_TIMEOUT_CODE.toString().getBytes(StandardCharsets.UTF_8));return response.writeWith(Flux.just(buffer)); // 异步响应错误}}}
}5、【Nacos控制台】所有的网关现在都是通过Nacos保存的配置项这样就需要在Nacos之中增加Token访问的路由地址
[{id: dept,uri: lb://dept.provider,order: 1,predicates: [{name: Path,args: {pattern: /provider/dept/**}}],filters: [{name: AddRequestHeader,args: {_genkey_0: Request-Token-Muyan,_genkey_1: www.yootk.com}}]},{id: token,uri: lb://token.provider,order: 1,predicates: [{name: Path,args: {pattern: /token/**}}]}
]6、 【Postman测试工具】访问Token 服务获取 Token信息:
gateway-9501:9501/token/create?midadminpasswordhello当前的路径不需要进行过滤器的排查而其他微服务需要进行过滤器排查
7、【common-api子模块】修改部门微服务的接口 改Mapping
GetMapping(/dept.provider/provider/dept/get/{deptno}) // 远程REST接口GetMapping(/provider/dept/get/{deptno}) // 远程REST接口package com.yootk.service;import com.yootk.common.dto.DeptDTO;
import com.yootk.service.config.FeignConfig;
import com.yootk.service.fallback.DeptServiceFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;import java.util.List;
import java.util.Map;
FeignClient(value microcloud.gateway, // 使用网关的名称进行访问configuration FeignConfig.class,// 定义要访问的微服务实例名称fallbackFactory DeptServiceFallbackFactory.class) // 部门降级配置
public interface IDeptService { // 业务接口/*** 根据部门的编号获取部门的完整信息* param id 要查询的部门编号* return 编号存在则以DTO对象的形式返回部门数据如果不存在返回null*/GetMapping(/provider/dept/get/{deptno}) // 远程REST接口public DeptDTO get(PathVariable(deptno) long id);/*** 增加部门对象* param dto 保存要增加部门的详细数据* return 增加成功返回true否则返回false*/PostMapping(/provider/dept/add)public boolean add(DeptDTO dto);/*** 列出所有的部门数据信息* return 全部数据的集合 如果没有任何的部门数据则集合为空size() 0*/GetMapping(/provider/dept/list)public ListDeptDTO list();/*** 进行部门的分页数据加载操作* param currentPage 当前所在页* param lineSize 每页加载的数据行数* param column 模糊查询的数据列* param keyword 模糊查询关键字* return 部门集合数据以及统计数据返回的数据项包括* 1、key allDepts、value List集合部门的全部数据对象* 2、key allRecorders、value 总记录数* 3、key allPages、value 页数。*/GetMapping(/provider/dept/split)public MapString, Object split(RequestParam(cp) int currentPage,RequestParam(ls) int lineSize,RequestParam(col) String column,RequestParam(kw) String keyword);
}
消费端获取JWT 现在已经成功的搭建TokenServer、微服务的授权检测、网关过滤检查那么剩下的部分就是要对消费端进行整改了毕竟,最终可以发布给外部的就是消费端。 1、【common-api子模块】创建Token 业务接口并映射Token 操作路径
package com.yootk.service;import com.yootk.common.dto.MemberDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;FeignClient(microcloud.gateway)
public interface IMemberTokenService {GetMapping(/token/create)public String login(MemberDTO memberDTO);
}2、【common-api子模块】因为后面可能会有微服务之间混合调用的情况所以可以考虑做一个公共的加载类。
package com.yootk.service.load;import com.yootk.common.dto.MemberDTO;
import com.yootk.service.IMemberTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;Component
Slf4j
public class FeignTokenLoaderRunner implements CommandLineRunner {Autowiredprivate IMemberTokenService memberTokenService; // 远程接口映射Overridepublic void run(String... args) throws Exception {MemberDTO dto new MemberDTO();dto.setMid(admin);dto.setPassword(hello);String token this.memberTokenService.login(dto); // 获取Tokenif (token ! null) { // 已经获取到了Token数据log.info(获取Token数据成功{}, token);System.setProperty(yootk.token, token); // 属性不允许为null}}
}3、【common-api子模块】修改Feign配置类由于每次请求时都需要传递JWT的数据内容所以可以配置一个拦截器。
package com.yootk.service.config;import feign.Logger;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;public class FeignConfig { // 定义Feign配置类Beanpublic Logger.Level level() {return Logger.Level.FULL; // 输出完全的日志信息}Beanpublic RequestInterceptor getFeignRequestInterceptor() { // 请求拦截器return (template - {template.header(serviceName, pc);// 将系统JVM进程保存的Token数据发送到目标请求端template.header(yootk-token, System.getProperty(yootk.token));});}
}4、【consumer-springboot-80子模块】修改程序启动类并定义FeignConfig配置类启动程序 defaultConfiguration FeignConfig.class
package com.yootk.consumer;import com.yootk.service.config.FeignConfig;
import muyan.yootk.config.ribbon.DeptProviderRibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;SpringBootApplication
EnableDiscoveryClient
// 如果此时要有多个配置项可以使用RibbonClients注解该注解可以配置多个RibbonClient
RibbonClient(name dept.provider, configuration DeptProviderRibbonConfig.class) // 自定义Ribbon配置
ComponentScan({com.yootk.service, com.yootk.consumer})
EnableFeignClients(basePackages {com.yootk.service}, defaultConfiguration FeignConfig.class) // Feign扫描包
public class StartConsumerApplication {public static void main(String[] args) {SpringApplication.run(StartConsumerApplication.class, args);}
}启动程序出现如下错误按要求设置spring.main
5、【comsuner-springboot-80子模块】修改application.yml配置文件添加一个配置覆盖的选项
spring:main:allow-bean-definition-overriding: true此时消费端启动时会通过远程接口实现 Token 数据的获取随后在每次进行访问的时候都会将此Token进行请求头信息的设置这样就实现了认证和授权的统一管理。
实际Token是由前端传过来的
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/88205.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!