Higress 之 JWT-Auth 微服务网关统一鉴权
概要
上编文章,应用接入了 Higress 网关
展示了 Higress --> Nacos --> demo-app --> demo-infra --> MySQL MGR
本篇,为应用接入鉴权,验证用户身份,保障资源API访问安全。
代码展示
构建文件添加
implementation 'org.bitbucket.b_c:jose4j:0.9.3'
创建 classpath privateKeyJson.json,publicKeyJson.json
运行 JWTHelper#rsaJsonWebKey 方法,对应日志生成存入 json文件
JWT 生成,登录认证方法调整
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JWTHelper {
@SneakyThrows
public static void rsaJsonWebKey() {
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
final String publicKeyJson = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
final String privateKeyJson = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
log.info("publicKeyJson: {}", publicKeyJson);
log.info("privateKeyJson: {}", privateKeyJson);
}
@SneakyThrows
public static String generateJwt(Map<String, String> claim) {
String keyId = "Today is Sunday.";
File file = new ClassPathResource("privateKeyJson.json").getFile();
String privateKeyJson = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
NumericDate date = NumericDate.now();
date.addSeconds(60 * 60 * 24 * 7);
claims.setExpirationTime(date);
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("demo-login");
claims.setAudience("all-users");
claims.setIssuer("Jazz"); // 此处和插件配置 issuer 保持一致
Optional.ofNullable(claim).ifPresent(v -> {
for (Map.Entry<String, String> entry : v.entrySet()) {
claims.setClaim(entry.getKey(), entry.getValue());
}
});
JsonWebSignature jws = new JsonWebSignature();
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
jws.setKeyIdHeaderValue(keyId);
jws.setPayload(claims.toJson());
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyJson)).getPrivateKey();
jws.setKey(privateKey);
return jws.getCompactSerialization();
}
}
@PostMapping("/accountLogin")
public Response accountLogin(HttpServletResponse response, @RequestBody AccountLoginQry accountLoginQry) {
Account account = accountService.accountLogin(accountLoginQry);
account.setMobile(DesensitizedUtil.mobilePhone(account.getMobile()));
Map<String, String> claim = new HashMap<>();
claim.put(Account.Fields.userName, account.getUserName());
claim.put(Account.Fields.mobile, account.getMobile());
response.addHeader("access_token", JWTHelper.generateJwt(claim));
return SingleResponse.of(account);
}
新增资源服务
@RequestMapping("/test")
@RestController
public class TestController {
@GetMapping("/hello")
public Response hello() {
return SingleResponse.of("资源服务测试");
}
}
在 Higress,路由配置 新增 test 路由,并开启 JWT-Auth 插件,配置如下
consumers:
- issuer: "Jazz"
jwks: |-
{
"keys": [
{
"kty": "RSA",
"n": "t8SUi0gOAJYiBZ-PMY6qNUESBBCBeicfjsqq2JLNIhRTPu-_W1Wzc02wuTtjkGbvLfvAM5n0yqwY8k5z_4CyO_dcf6nLfzCVby2IPvsSTFjbiftjyOJYgdeNdGkKj3wYD1WQH_SKAlD3dCMKGCxOwNw0AM76MuhRHx1OTNYR86CG6dAJhHHkCvqtookFVXL7ecwnvpqlNf-cFJz6Ibdgf6Siz-fjGq3lMHJlE0ZF-0LxW23FGWgSmR5oqG8Zhe2zg0J5xKqQsCnaJ4CM75XWtGkZjsdgSxCXaCNuhRkPcPG_tRTWZdRBqOtJDEJuKZOuoRayt_6EDShx8TNpmlshkQ",
"e": "AQAB"
}
]
}
name: "consumer1"
global_auth: false
issuer 为 JWTHelper#generateJwt claims.setIssuer("Jazz")
插件配置 keys 括号内容为 publicKeyJson.json 内容
至此,访问 /test/hello 资源需要权限
通过 用户名/密码 访问 /demo/accountLogin 登录认证换取 JWT (添加在响应头)
访问 /test/hello 在 url或者header 携带JWT,即可获取资源数据
url 参数为 access_token
header 参数为 Authorization 参数值格式 Bearer JWT