refactor: 策略模式重构用户认证授权功能

This commit is contained in:
xiongxiaoyang 2022-05-18 10:12:17 +08:00
parent cdd1455ab9
commit d2e7590646
8 changed files with 209 additions and 24 deletions

View File

@ -0,0 +1,27 @@
package io.github.xxyopen.novel.core.auth;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 平台后台管理系统 认证策略
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@Component
@RequiredArgsConstructor
public class AdminAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper;
@Override
public void auth(String token) throws BusinessException {
// TODO 平台后台 token 校验
}
}

View File

@ -0,0 +1,53 @@
package io.github.xxyopen.novel.core.auth;
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.entity.UserInfo;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import java.util.Objects;
/**
* 策略模式实现用户认证授权功能
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
public interface AuthStrategy {
/**
* 请求用户认证
*
* @param token 登录 token
* @throws BusinessException 认证失败则抛出义务异常
*/
void auth(String token) throws BusinessException;
/**
* 前台多系统单点登录统一账号认证门户系统作家系统以及后面会扩展的漫画系统和视频系统等
*
* @param jwtUtils jwt 工具
* @param userInfoMapper 用户查询 Mapper
* @param token token 登录 token
* @return 用户ID
*/
default Long authSSO(JwtUtils jwtUtils, UserInfoMapper userInfoMapper, String token) {
if (Objects.isNull(token)) {
// token 为空
throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED);
}
Long userId = jwtUtils.parseToken(token, SystemConfigConsts.NOVEL_FRONT_KEY);
if (Objects.isNull(userId)) {
// token 解析失败
throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED);
}
UserInfo userInfo = userInfoMapper.selectById(userId);
if (Objects.isNull(userInfo)) {
// 用户不存在
throw new BusinessException(ErrorCodeEnum.USER_ACCOUNT_NOT_EXIST);
}
return userId;
}
}

View File

@ -0,0 +1,48 @@
package io.github.xxyopen.novel.core.auth;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.entity.AuthorInfo;
import io.github.xxyopen.novel.dao.mapper.AuthorInfoMapper;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 作家后台管理系统 认证策略
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@Component
@RequiredArgsConstructor
public class AuthorAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper;
private final AuthorInfoMapper authorInfoMapper;
@Override
public void auth(String token) throws BusinessException {
// 统一账号认证
Long userId = authSSO(jwtUtils, userInfoMapper, token);
// 作家权限认证
QueryWrapper<AuthorInfo> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(DatabaseConsts.AuthorInfoTable.ColumnEnum.USER_ID.getName(), userId)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
AuthorInfo authorInfo = authorInfoMapper.selectOne(queryWrapper);
if(Objects.isNull(authorInfo)){
// 作家账号不存在无权访问作家专区
throw new BusinessException(ErrorCodeEnum.USER_UN_AUTH);
}
}
}

View File

@ -0,0 +1,28 @@
package io.github.xxyopen.novel.core.auth;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 前台门户系统 认证策略
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@Component
@RequiredArgsConstructor
public class FrontAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper;
@Override
public void auth(String token) throws BusinessException {
// 统一账号认证
authSSO(jwtUtils,userInfoMapper,token);
}
}

View File

@ -77,6 +77,11 @@ public enum ErrorCodeEnum {
* */
USER_LOGIN_EXPIRED("A0230","用户登录已过期"),
/**
* 访问未授权
* */
USER_UN_AUTH("A0301","访问未授权"),
/**
* 一级宏观错误码系统执行出错
* */

View File

@ -32,7 +32,8 @@ public class WebConfig implements WebMvcConfigurer {
, ApiRouterConsts.API_ADMIN_URL_PREFIX + "/**")
// 放行登录注册相关请求接口
.excludePathPatterns(ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/register"
, ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/login",
ApiRouterConsts.API_ADMIN_URL_PREFIX + "/login");
, ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/login"
, ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/register"
,ApiRouterConsts.API_ADMIN_URL_PREFIX + "/login");
}
}

View File

@ -31,6 +31,26 @@ public class DatabaseConsts {
}
/**
* 作家信息表
*/
public static class AuthorInfoTable {
@Getter
public enum ColumnEnum {
USER_ID("user_id");
private String name;
ColumnEnum(String name) {
this.name = name;
}
}
}
/**
* 小说类别表
*/

View File

@ -1,11 +1,12 @@
package io.github.xxyopen.novel.core.intercepter;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.xxyopen.novel.core.auth.AuthStrategy;
import io.github.xxyopen.novel.core.common.constant.ErrorCodeEnum;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import io.github.xxyopen.novel.core.util.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
@ -14,7 +15,7 @@ import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.Map;
/**
* 认证 拦截器
@ -27,34 +28,36 @@ import java.util.Objects;
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final JwtUtils jwtUtils;
private final Map<String,AuthStrategy> authStrategy;
private final ObjectMapper objectMapper;
@SuppressWarnings("NullableProblems")
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 校验登录JWT
// 获取登录 JWT
String token = request.getHeader(SystemConfigConsts.HTTP_AUTH_HEADER_NAME);
if (!Objects.isNull(token)) {
String requestUri = request.getRequestURI();
if (requestUri.contains(ApiRouterConsts.API_FRONT_USER_URL_PREFIX)
|| requestUri.contains(ApiRouterConsts.API_AUTHOR_URL_PREFIX)) {
// 校验会员和作家的登录权限
Long userId = jwtUtils.parseToken(token, SystemConfigConsts.NOVEL_FRONT_KEY);
if (!Objects.isNull(userId)) {
// TODO 查询用户信息并校验账号状态是否正常
// 认证成功
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}else{
// TODO 校验后台的登录权限
}
}
// 获取请求的 URI
String requestUri = request.getRequestURI();
// 根据请求的 URI 得到认证策略
String authStrategyName = requestUri.substring(ApiRouterConsts.API_URL_PREFIX.length() + 1);
authStrategyName = authStrategyName.substring(0,authStrategyName.indexOf("/"));
authStrategyName = String.format("%sAuthStrategy",authStrategyName);
// 开始认证
try {
authStrategy.get(authStrategyName).auth(token);
}catch (BusinessException exception){
// 认证失败
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(objectMapper.writeValueAsString(RestResp.fail(ErrorCodeEnum.USER_LOGIN_EXPIRED)));
return false;
}
// 认证成功
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}