通过 Spring Security + OAuth2 认证和鉴权,每次请求都需要经过 OAuth Server 验证当前 token 的合法性,并且需要查询该 token 对应的用户权限,在高并发场景下会存在性能瓶颈。使用 JWT 的方式,OAuth Server 只验证一次,用户所有信息 (包括权限) 包含在返回的 JWT 中
准备工作
生成公钥、私钥
私钥
在控制台输入命令:
1 | keytool -genkeypair -alias spring-jwt -validity 3650 -keyalg RSA -dname "CN=Victor,OU=Karonda,O=Karonda,L=Shenzhen,S=Guangdong,C=CN" -keypass abc123 -storepass abc123 -keystore spring-jwt.jks |
各个参数的含义,可以通过命令查看:
1 | keytool -genkeypair -help |
其中 DName 各个参数代表的意义见: X.500 Distinguished Names
公钥
在控制台输入命令:
1 | keytool -list -rfc --keystore spring-jwt.jks | openssl x509 -inform pem -pubkey |
会提示输入密码,密码为生成私钥命令里设置的密码
本文生成的公钥:
1 | -----BEGIN PUBLIC KEY----- |
新建 public.cert 文件保存上面生成的公钥 (要包含公钥的完整信息,即 BEGIN PUBLIC KEY 和 END PUBLIC KEY 部分也要包含在文件中)
Windows 系统需要先安装 OpenSSL: 下载链接
将私钥和公钥分别拷贝到 oauth2-server 和 eureka-client 的 resources 目录下
并在 oauth2-server 和 eureka-client 的 pom 添加配置:
1 | <plugin> |
因为密钥文件不需要编译
oauth2-server
修改 Authorization Server 配置
1 | @Configuration |
eureka-client
上一篇文章中的 OAuth2 Client 配置本文用不到,要移除
Resource Server 配置
配置 JWT 转换器
1 | @Configuration |
修改 Resource Server 配置
1 | @Configuration |
JWT 类
1 | public class JWT { |
Feign 客户端
1 | @FeignClient("oauth2-server") |
同时需要在启动类添加注解:
1 | @EnableFeignClients |
DTO 及异常处理
1 | public class UserLoginDTO { |
1 | public class UserLoginException extends RuntimeException { |
1 | @ControllerAdvice // 表明该类是异常统一处理类 |
service & controller
添加登录方法
1 | @Override |
1 | @RequestMapping(value = "/login", method = RequestMethod.POST) |
测试
- 启动 eureka-server
- 启动 oauth2-server
- 启动 config-server
- 启动 eureka-client
先取消授权:
1 | DELETE FROM user_role WHERE user_id = 2; |
使用 Postman 测试:
用户登录
| - | POST | localhost:8011/user/login |
| Body | ||
| - | username | admin |
| - | password | 123 |
1 | { |
访问不需要权限的接口
| - | GET | localhost:8011/hi?name=Victor |
| Headers | ||
| - | Authorization | Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjEyNjUyMzgsInVzZXJfbmFtZSI6ImFkbWluIiwianRpIjoiNTQxMDk3MDItNTU1Yy00NjA4LThkYzYtNzU3NWZjNGIwNGIyIiwiY2xpZW50X2lkIjoiZXVyZWthLWNsaWVudCIsInNjb3BlIjpbInNlcnZlciJdfQ.P6dtT76bFyQ6aF7-v6Vphi3ivLR0x4w739gwmBRGujaRpfDMjwQHCn5REyxEOAKdoxrVT__v73qcb78_8Ovb97L13ztnzdlPmLYzcAkQdMFz78yAjZIp2VtzxZ87Ecmk9f6-bIRlBxS9A24t0y4Tp1gkPITB1vxod0FewAHCsUJQ9WqLNeW9bxzZvy5DtlJlCCY7lOIjfDxlQdXygpwznZ4rIHv-O-eOr2aqcKMLZhdtW7hHsy2JccIUm1ZdpVQfUMD7XzWFAQoZYFLc0oXyVL0nFasOr-Ne1UR1iZYI4cS-ONVLMe78erVb-zRoyTAhEb7Pkyepkwm_Xv23U2CoeA |
1 | Hello Victor, from port: 8011, version: 1.0.2 |
访问需要权限的接口
| - | GET | localhost:8011/hello |
| Headers | ||
| - | Authorization | Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjEyNjUyMzgsInVzZXJfbmFtZSI6ImFkbWluIiwianRpIjoiNTQxMDk3MDItNTU1Yy00NjA4LThkYzYtNzU3NWZjNGIwNGIyIiwiY2xpZW50X2lkIjoiZXVyZWthLWNsaWVudCIsInNjb3BlIjpbInNlcnZlciJdfQ.P6dtT76bFyQ6aF7-v6Vphi3ivLR0x4w739gwmBRGujaRpfDMjwQHCn5REyxEOAKdoxrVT__v73qcb78_8Ovb97L13ztnzdlPmLYzcAkQdMFz78yAjZIp2VtzxZ87Ecmk9f6-bIRlBxS9A24t0y4Tp1gkPITB1vxod0FewAHCsUJQ9WqLNeW9bxzZvy5DtlJlCCY7lOIjfDxlQdXygpwznZ4rIHv-O-eOr2aqcKMLZhdtW7hHsy2JccIUm1ZdpVQfUMD7XzWFAQoZYFLc0oXyVL0nFasOr-Ne1UR1iZYI4cS-ONVLMe78erVb-zRoyTAhEb7Pkyepkwm_Xv23U2CoeA |
1 | { |
手动授权:
1 | INSERT INTO user_role (user_id, role_id) VALUES (2, 2); |
重新登录后再次访问接口
1 | hello! |
完整代码:GitHub
本人 C# 转 Java 的 newbie, 如有错误或不足欢迎指正,谢谢