目的
- 校验令牌的有效性,expire是否过期
- 令牌的持有者是谁
流程
考虑到前端访问后端api时,都需要进行token验证,可以通过一个过滤器来执行
项目结构:
1.新建OAuth2CheckTokenRespDTO类
对应的是图 2.2 返回值 —> 是否有效,userId等等
2.新增 checkToken方法
在OAuth2Client中新增该方法,校验访问令牌,并返回基本信息 —> 图2.1
- 构建请求头时,将租户id放进去,这里默认是 TENANT_ID = 1,可根据实际情况调整
- 将拿到的token放到请求参数中
- 执行请求,访问的是 “/check-token” 接口,返回的结果就是 OAuth2AccessTokenRespDTO类
对应的 “/check-token” 接口是 OAuth2OpenController 下的 checkToken :
3.新建 TokenAuthenticationFilter ,LoginUser 和 SecurityUtils
LoginUser: 登录完之后或者说token校验后会把 LoginUser 创建出来,然后放到线程的上下文里面去,后续如果需要获取userId,可以从上下文中拿到 LoginUser,便可以拿到对应的userId
新建SecurityUtils:
具体是在TokenAuthenticationFilter中
/**
* 安全服务工具类
*
* @author 芋道源码
*/
public class SecurityUtils {
public static final String AUTHORIZATION_BEARER = "Bearer";
private SecurityUtils() {}
/**
* 从请求中,获得认证 Token
*
* @param request 请求
* @param header 认证 Token 对应的 Header 名字
* @return 认证 Token
*/
public static String obtainAuthorization(HttpServletRequest request, String header) {
String authorization = request.getHeader(header);
if (!StringUtils.hasText(authorization)) {
return null;
}
int index = authorization.indexOf(AUTHORIZATION_BEARER + " ");
if (index == -1) { // 未找到
return null;
}
return authorization.substring(index + 7).trim();
}
/**
* 获得当前认证信息
*
* @return 认证信息
*/
public static Authentication getAuthentication() {
SecurityContext context = SecurityContextHolder.getContext();
if (context == null) {
return null;
}
return context.getAuthentication();
}
/**
* 获取当前用户
*
* @return 当前用户
*/
@Nullable
public static LoginUser getLoginUser() {
Authentication authentication = getAuthentication();
if (authentication == null) {
return null;
}
return authentication.getPrincipal() instanceof LoginUser ? (LoginUser) authentication.getPrincipal() : null;
}
/**
* 获得当前用户的编号,从上下文中
*
* @return 用户编号
*/
@Nullable
public static Long getLoginUserId() {
LoginUser loginUser = getLoginUser();
return loginUser != null ? loginUser.getId() : null;
}
/**
* 设置当前用户
*
* @param loginUser 登录用户
* @param request 请求
*/
public static void setLoginUser(LoginUser loginUser, HttpServletRequest request) {
// 创建 Authentication,并设置到上下文
Authentication authentication = buildAuthentication(loginUser, request);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
private static Authentication buildAuthentication(LoginUser loginUser, HttpServletRequest request) {
// 创建 UsernamePasswordAuthenticationToken 对象
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginUser, null, Collections.emptyList());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
return authenticationToken;
}
}
TokenAuthenticationFilter:
doFilterInternal:
- 先从 Authorization 请求头中拿到 token
- 构建 LoginUser 通过buildLoginUserByToken方法,再通过调用 checkToken的方法最后获取 OAuth2CheckTokenRespDTO,并将里面的值赋给LoginUser
- 将LoginUser设置到上下文中
- 最好如果你想要获取 LoginUser或者userId,可直接调用 SecurityUtils的 getLoginUSer 和getLoginUserId 方法,即可从上下文或获取
4.将 TokenAuthenticationFilter 添加到 SecurityConfiguration 中
这样我们就完成了最后配置