前言:本文作为Spring Security-整合Spring Boot(分布式)-篇章一,主要是为了后面的具体代码篇章做铺垫。JWT、RSA,以及框架内业务逻辑代码是需要提前了解的,这些内容是合理使用Spring Security的关键。
零、本文纲要
一、 系统安全
- JWT
- RSA非对称加密
二、 SpringSecurity + JWT + RSA分布式认证 - SpringSecurity的用户认证逻辑
- SpringSecurity的身份校验逻辑
一、 系统安全
为了保障系统安全,我们使用Spring Security框架做认证授权。使用JWT和RSA技术,进而确保认证授权信息不被外界修改,进而突破权限管理越界使用。
1. JWT
- ① JWT构成
JWT: Header.Payload.Signature
Ⅰ 头部Header
主要设置一些规范信息,签名部分的编码格式就在头部中声明;
{
"alg": "HS256",
"typ": "JWT"
}
Ⅱ 载荷Payload
token中存放有效信息的部分,比如用户名,用户角色,过期时间等;
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Ⅲ 签名Signature
a、将头部与载荷分别采用【base64编码】后,用“.”相连;
b、再加入【盐】;(生成token与解析token的盐一样,则是对称加密)
c、最后使用头部声明的编码类型进行【编码】,就得到了签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
JWT =
base64UrlEncode(Header).
base64UrlEncode(Payload).
base64UrlEncode(HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret))
注意:
① HS256(采用 SHA-256 的 HMAC 签名)是一种对称算法,Hash-based Message Authentication Code(哈希运算消息认证码)
② RS256 (采用 SHA-256 的 RSA 签名) 是一种非对称算法,它使用公共/私钥对: 标识提供方采用私钥生成签名, JWT 的使用方获取公钥以验证签名。
2. RSA非对称加密
- ① 【重点】加密规则
私钥加密,持有私钥或公钥才可以解密;
公钥加密,持有私钥才可解密。
这一块内容本人以前很容易绕进去,所以展开来简单讲下。
客户端 → 服务器A(Auth Server:标识提供方) → 客户端 → 服务器B(Source:JWT使用方)
假设:
情形一:服务器A公钥加密,私钥对公
a、客户端登录请求服务器A,认证后Payload只有"ROLE_USER"权限,返回公钥加密token;
b、由于私钥对公,客户端使用私钥解密,给自身添加"ROLE_ADMIN"权限,再加密;
c、客户端请求服务器B(Source:JWT使用方),私钥对公,此时服务器B可以使用私钥解密,授权"ROLE_USER"和"ROLE_ADMIN"给用户。
情形二:服务器A私钥加密,公钥对公
a、客户端登录请求服务器A,认证后Payload只有"ROLE_USER"权限,返回私钥加密token;
b、此时公钥对公,客户端使用公钥解密,给自身添加"ROLE_ADMIN"权限,再加密;
c、客户端请求服务器B(Source:JWT使用方),公钥对公,此时服务器B持有公钥,不能解密公钥加密内容。
所以,标识提供方应采用私钥加密,而公钥则是提供给使用方的。
二、 SpringSecurity + JWT + RSA分布式认证
1. SpringSecurity的用户认证逻辑
- ① UsernamePasswordAuthenticationFilter#attemptAuthentication
可以看到认证业务要求的是POST表单请求提交数据。而分布式前后端分离情形下,我们基本使用AJAX请求,而不是POST表单提交。所以此处的业务逻辑需要我们重写。
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
...
}
}
- ② AbstractAuthenticationProcessingFilter
doFilter方法会进行具体的认证业务
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
...
successfulAuthentication(request, response, chain, authResult);
}
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
...
//认证通过会把结果存入Spring Security自己的容器中
SecurityContextHolder.getContext().setAuthentication(authResult);
...
}
认证通过的原有逻辑也不是我们需要的,所以此处也需要进行响应的修改。
2. SpringSecurity的身份校验逻辑
- ① BasicAuthenticationFilter
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
final boolean debug = this.logger.isDebugEnabled();
String header = request.getHeader("Authorization");
if (header == null || !header.toLowerCase().startsWith("basic ")) {
chain.doFilter(request, response);
return;
}
try {
//【断点】对请求头内的信息做解码,可以看一下
String[] tokens = extractAndDecodeHeader(header, request);
...
}
...
}
Spring Security的解码逻辑还是非常清晰的值得学习,此处我们还需要跟据自己的需求坐下调整。
三、结尾
以上即为Spring Security-整合Spring Boot(分布式)-篇章一的全部内容,感谢阅读。
网友评论