perf: 优化用户认证流程

This commit is contained in:
xiongxiaoyang 2022-05-18 16:22:11 +08:00
parent 2d7b3790b9
commit 9fc5aabf36
13 changed files with 177 additions and 42 deletions

View File

@ -98,13 +98,6 @@
<artifactId>hibernate-validator</artifactId> <artifactId>hibernate-validator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>

View File

@ -3,17 +3,18 @@ package io.github.xxyopen.novel.controller.front;
import io.github.xxyopen.novel.core.auth.UserHolder; import io.github.xxyopen.novel.core.auth.UserHolder;
import io.github.xxyopen.novel.core.common.resp.RestResp; import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts; 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 io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dto.req.UserInfoUptReqDto; import io.github.xxyopen.novel.dto.req.UserInfoUptReqDto;
import io.github.xxyopen.novel.dto.req.UserLoginReqDto; import io.github.xxyopen.novel.dto.req.UserLoginReqDto;
import io.github.xxyopen.novel.dto.req.UserRegisterReqDto; import io.github.xxyopen.novel.dto.req.UserRegisterReqDto;
import io.github.xxyopen.novel.dto.resp.UserLoginRespDto; import io.github.xxyopen.novel.dto.resp.UserLoginRespDto;
import io.github.xxyopen.novel.service.UserService; import io.github.xxyopen.novel.service.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/** /**
* 会员模块相关 控制器 * 会员模块相关 控制器

View File

@ -1,8 +1,6 @@
package io.github.xxyopen.novel.core.auth; package io.github.xxyopen.novel.core.auth;
import io.github.xxyopen.novel.core.common.exception.BusinessException; 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 lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -16,10 +14,6 @@ import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
public class AdminAuthStrategy implements AuthStrategy { public class AdminAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper;
@Override @Override
public void auth(String token) throws BusinessException { public void auth(String token) throws BusinessException {
// TODO 平台后台 token 校验 // TODO 平台后台 token 校验

View File

@ -4,8 +4,8 @@ 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.exception.BusinessException;
import io.github.xxyopen.novel.core.constant.SystemConfigConsts; import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import io.github.xxyopen.novel.core.util.JwtUtils; import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.entity.UserInfo; import io.github.xxyopen.novel.dto.UserInfoDto;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper; import io.github.xxyopen.novel.manager.UserInfoCacheManager;
import java.util.Objects; import java.util.Objects;
@ -19,6 +19,7 @@ public interface AuthStrategy {
/** /**
* 请求用户认证 * 请求用户认证
* 如果后面需要扩展到对每一个URI都进行权限控制那么此方法可以加一个参数来接收用户请求的URI
* *
* @param token 登录 token * @param token 登录 token
* @throws BusinessException 认证失败则抛出义务异常 * @throws BusinessException 认证失败则抛出义务异常
@ -29,11 +30,11 @@ public interface AuthStrategy {
* 前台多系统单点登录统一账号认证门户系统作家系统以及后面会扩展的漫画系统和视频系统等 * 前台多系统单点登录统一账号认证门户系统作家系统以及后面会扩展的漫画系统和视频系统等
* *
* @param jwtUtils jwt 工具 * @param jwtUtils jwt 工具
* @param userInfoMapper 用户查询 Mapper * @param userInfoCacheManager 用户缓存管理对象
* @param token token 登录 token * @param token token 登录 token
* @return 用户ID * @return 用户ID
*/ */
default Long authSSO(JwtUtils jwtUtils, UserInfoMapper userInfoMapper, String token) { default Long authSSO(JwtUtils jwtUtils, UserInfoCacheManager userInfoCacheManager, String token) {
if (Objects.isNull(token)) { if (Objects.isNull(token)) {
// token 为空 // token 为空
throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED); throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED);
@ -43,7 +44,7 @@ public interface AuthStrategy {
// token 解析失败 // token 解析失败
throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED); throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED);
} }
UserInfo userInfo = userInfoMapper.selectById(userId); UserInfoDto userInfo = userInfoCacheManager.getUser(userId);
if (Objects.isNull(userInfo)) { if (Objects.isNull(userInfo)) {
// 用户不存在 // 用户不存在
throw new BusinessException(ErrorCodeEnum.USER_ACCOUNT_NOT_EXIST); throw new BusinessException(ErrorCodeEnum.USER_ACCOUNT_NOT_EXIST);

View File

@ -1,13 +1,11 @@
package io.github.xxyopen.novel.core.auth; 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.constant.ErrorCodeEnum;
import io.github.xxyopen.novel.core.common.exception.BusinessException; 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.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.entity.AuthorInfo; import io.github.xxyopen.novel.dto.AuthorInfoDto;
import io.github.xxyopen.novel.dao.mapper.AuthorInfoMapper; import io.github.xxyopen.novel.manager.AuthorInfoCacheManager;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper; import io.github.xxyopen.novel.manager.UserInfoCacheManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -25,21 +23,17 @@ public class AuthorAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils; private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper; private final UserInfoCacheManager userInfoCacheManager;
private final AuthorInfoMapper authorInfoMapper; private final AuthorInfoCacheManager authorInfoCacheManager;
@Override @Override
public void auth(String token) throws BusinessException { public void auth(String token) throws BusinessException {
// 统一账号认证 // 统一账号认证
Long userId = authSSO(jwtUtils, userInfoMapper, token); Long userId = authSSO(jwtUtils, userInfoCacheManager, token);
// 作家权限认证 // 作家权限认证
QueryWrapper<AuthorInfo> queryWrapper = new QueryWrapper<>(); AuthorInfoDto authorInfo = authorInfoCacheManager.getAuthor(userId);
queryWrapper
.eq(DatabaseConsts.AuthorInfoTable.ColumnEnum.USER_ID.getName(), userId)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
AuthorInfo authorInfo = authorInfoMapper.selectOne(queryWrapper);
if(Objects.isNull(authorInfo)){ if(Objects.isNull(authorInfo)){
// 作家账号不存在无权访问作家专区 // 作家账号不存在无权访问作家专区
throw new BusinessException(ErrorCodeEnum.USER_UN_AUTH); throw new BusinessException(ErrorCodeEnum.USER_UN_AUTH);

View File

@ -2,7 +2,7 @@ package io.github.xxyopen.novel.core.auth;
import io.github.xxyopen.novel.core.common.exception.BusinessException; import io.github.xxyopen.novel.core.common.exception.BusinessException;
import io.github.xxyopen.novel.core.util.JwtUtils; import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper; import io.github.xxyopen.novel.manager.UserInfoCacheManager;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -18,11 +18,11 @@ public class FrontAuthStrategy implements AuthStrategy {
private final JwtUtils jwtUtils; private final JwtUtils jwtUtils;
private final UserInfoMapper userInfoMapper; private final UserInfoCacheManager userInfoCacheManager;
@Override @Override
public void auth(String token) throws BusinessException { public void auth(String token) throws BusinessException {
// 统一账号认证 // 统一账号认证
authSSO(jwtUtils,userInfoMapper,token); authSSO(jwtUtils,userInfoCacheManager,token);
} }
} }

View File

@ -1,7 +1,7 @@
package io.github.xxyopen.novel.core.config; package io.github.xxyopen.novel.core.config;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts; import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.core.intercepter.AuthInterceptor; import io.github.xxyopen.novel.core.interceptor.AuthInterceptor;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

View File

@ -84,6 +84,16 @@ public class CacheConsts {
* */ * */
public static final String IMG_VERIFY_CODE_CACHE_KEY = REDIS_CACHE_PREFIX + "imgVerifyCodeCache::"; public static final String IMG_VERIFY_CODE_CACHE_KEY = REDIS_CACHE_PREFIX + "imgVerifyCodeCache::";
/**
* 用户信息缓存
*/
public static final String USER_INFO_CACHE_NAME = "userInfoCache";
/**
* 作家信息缓存
*/
public static final String AUTHOR_INFO_CACHE_NAME = "authorInfoCache";
/** /**
* 缓存配置常量 * 缓存配置常量
*/ */
@ -109,7 +119,11 @@ public class CacheConsts {
BOOK_CONTENT_CACHE(2, BOOK_CONTENT_CACHE_NAME, 60 * 60 * 12, 3000), BOOK_CONTENT_CACHE(2, BOOK_CONTENT_CACHE_NAME, 60 * 60 * 12, 3000),
LAST_UPDATE_BOOK_ID_LIST_CACHE(0,LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME,60 * 60, 10); LAST_UPDATE_BOOK_ID_LIST_CACHE(0,LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME,60 * 60, 10),
USER_INFO_CACHE(2,USER_INFO_CACHE_NAME,60 * 60 * 24, 10000),
AUTHOR_INFO_CACHE(2,AUTHOR_INFO_CACHE_NAME,60 * 60 * 48, 1000);
/** /**
* 缓存类型 0-本地 1-本地和远程 2-远程 * 缓存类型 0-本地 1-本地和远程 2-远程

View File

@ -1,9 +1,8 @@
package io.github.xxyopen.novel.core.intercepter; package io.github.xxyopen.novel.core.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.xxyopen.novel.core.auth.AuthStrategy; import io.github.xxyopen.novel.core.auth.AuthStrategy;
import io.github.xxyopen.novel.core.auth.UserHolder; import io.github.xxyopen.novel.core.auth.UserHolder;
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.exception.BusinessException;
import io.github.xxyopen.novel.core.common.resp.RestResp; import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts; import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
@ -11,7 +10,6 @@ import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.HandlerInterceptor;

View File

@ -0,0 +1,26 @@
package io.github.xxyopen.novel.dto;
import lombok.Builder;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 作家信息 DTO
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@Data
@Builder
public class AuthorInfoDto implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long id;
private Integer status;
}

View File

@ -0,0 +1,26 @@
package io.github.xxyopen.novel.dto;
import lombok.Builder;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 用户信息 DTO
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@Data
@Builder
public class UserInfoDto implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Long id;
private Integer status;
}

View File

@ -0,0 +1,47 @@
package io.github.xxyopen.novel.manager;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.github.xxyopen.novel.core.constant.CacheConsts;
import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.AuthorInfo;
import io.github.xxyopen.novel.dao.mapper.AuthorInfoMapper;
import io.github.xxyopen.novel.dto.AuthorInfoDto;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 作家信息 缓存管理类
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
@Component
@RequiredArgsConstructor
public class AuthorInfoCacheManager {
private final AuthorInfoMapper authorInfoMapper;
/**
* 查询作家信息并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER
, value = CacheConsts.AUTHOR_INFO_CACHE_NAME)
public AuthorInfoDto getAuthor(Long userId) {
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)) {
return null;
}
return AuthorInfoDto.builder()
.id(authorInfo.getId())
.status(authorInfo.getStatus()).build();
}
}

View File

@ -0,0 +1,41 @@
package io.github.xxyopen.novel.manager;
import io.github.xxyopen.novel.core.constant.CacheConsts;
import io.github.xxyopen.novel.dao.entity.UserInfo;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import io.github.xxyopen.novel.dto.UserInfoDto;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 用户信息 缓存管理类
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
@Component
@RequiredArgsConstructor
public class UserInfoCacheManager {
private final UserInfoMapper userInfoMapper;
/**
* 查询用户信息并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER
, value = CacheConsts.USER_INFO_CACHE_NAME)
public UserInfoDto getUser(Long userId) {
UserInfo userInfo = userInfoMapper.selectById(userId);
if(Objects.isNull(userInfo)){
return null;
}
return UserInfoDto.builder()
.id(userInfo.getId())
.status(userInfo.getStatus()).build();
}
}