优化登录 token 校验:

1、后端新增 /api/v1/user/info 接口,优化 Oauth2Filter.getRequestToken 逻辑;
2、前端 /api/v1/user/login 登录成功后,保存 token 至浏览器本地;
3、前端请求添加本地 token。
This commit is contained in:
pengzhisheng
2025-03-14 09:54:54 +08:00
parent 40eb4ea4ec
commit 5b2957e7eb
14 changed files with 131 additions and 22 deletions
@@ -78,6 +78,8 @@ public interface Constant {
*/
String TOKEN_HEADER = "token";
String AUTHORIZATION = "Authorization";
/**
* 路径分割符
*/
@@ -58,6 +58,12 @@ public class ShiroConfig {
filters.put("oauth2", new Oauth2Filter());
shiroFilter.setFilters(filters);
//添加Shiro的内置过滤器
/*anon:无需认证就可以访问
authc:必须认证了才能让问
user:必须拥有,记住我功能,才能访问
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问*/
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon");
@@ -4,11 +4,16 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.page.TokenDTO;
import xiaozhi.common.utils.Result;
import xiaozhi.common.validator.AssertUtils;
import xiaozhi.modules.security.dao.SysUserTokenDao;
import xiaozhi.modules.security.dto.LoginDTO;
import xiaozhi.modules.security.password.PasswordUtils;
import xiaozhi.modules.security.service.CaptchaService;
@@ -31,6 +36,7 @@ public class LoginController {
private final SysUserTokenService sysUserTokenService;
private final CaptchaService captchaService;
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
@GetMapping("/captcha")
@Operation(summary = "验证码")
@@ -44,7 +50,7 @@ public class LoginController {
@PostMapping("/login")
@Operation(summary = "登录")
public Result login( @RequestBody LoginDTO login) {
public Result<TokenDTO> login(@RequestBody LoginDTO login) {
// 验证是否正确输入验证码
boolean validate = captchaService.validate(login.getCaptchaId(), login.getCaptcha());
if (!validate) {
@@ -84,4 +90,21 @@ public class LoginController {
}
@GetMapping("/info")
@Operation(summary = "用户信息获取")
public Result<SysUserDTO> info(@RequestHeader("Authorization")String authorization) {
logger.info("the authorization:{}", authorization);
String token;
if (StringUtils.isBlank(authorization) && authorization.contains("Bearer ")) {
throw new RenException(ErrorCode.UNAUTHORIZED);
}
token = authorization.replace("Bearer ", "");
if (StringUtils.isBlank(token)) {
throw new RenException(ErrorCode.UNAUTHORIZED);
}
SysUserDTO sysUserDTO = sysUserTokenService.getUserByToken(token);
Result result = new Result<SysUserDTO>();
return result.ok(sysUserDTO);
}
}
@@ -11,6 +11,7 @@ import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.utils.HttpContextUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.Result;
@@ -89,12 +90,22 @@ public class Oauth2Filter extends AuthenticatingFilter {
* 获取请求的token
*/
private String getRequestToken(HttpServletRequest httpRequest) {
String token;
//从header中获取token
String token = httpRequest.getHeader(Constant.TOKEN_HEADER);
String authorization = httpRequest.getHeader(Constant.AUTHORIZATION);
if (StringUtils.isBlank(authorization) && authorization.contains("Bearer ")) {
throw new RenException(ErrorCode.UNAUTHORIZED);
}
token = authorization.replace("Bearer ", "");
//如果header中不存在token,则从参数中获取token
if (StringUtils.isBlank(token)) {
token = httpRequest.getParameter(Constant.TOKEN_HEADER);
authorization = httpRequest.getParameter(Constant.AUTHORIZATION);
if (StringUtils.isBlank(authorization) && authorization.contains("Bearer ")) {
throw new RenException(ErrorCode.UNAUTHORIZED);
}
token = authorization.replace("Bearer ", "");
}
return token;
}
@@ -6,12 +6,15 @@ import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.user.UserDetail;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.MessageUtils;
import xiaozhi.modules.security.controller.LoginController;
import xiaozhi.modules.security.entity.SysUserTokenEntity;
import xiaozhi.modules.security.service.ShiroService;
import xiaozhi.modules.sys.entity.SysUserEntity;
@@ -30,6 +33,8 @@ public class Oauth2Realm extends AuthorizingRealm {
@Resource
private ShiroService shiroService;
private static final Logger logger = LoggerFactory.getLogger(Oauth2Realm.class);
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof Oauth2Token;
@@ -82,6 +87,11 @@ public class Oauth2Realm extends AuthorizingRealm {
userDetail.setToken(accessToken);
//账号锁定
if (userDetail.getStatus() == null) {
logger.error("账号状态异常,status 不能为空");
throw new DisabledAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_DISABLE));
}
if (userDetail.getStatus() == 0) {
throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK));
}
@@ -1,9 +1,11 @@
package xiaozhi.modules.security.service;
import xiaozhi.common.page.PageData;
import xiaozhi.common.page.TokenDTO;
import xiaozhi.common.service.BaseService;
import xiaozhi.common.utils.Result;
import xiaozhi.modules.security.entity.SysUserTokenEntity;
import xiaozhi.modules.sys.dto.SysUserDTO;
import java.util.Map;
@@ -19,7 +21,9 @@ public interface SysUserTokenService extends BaseService<SysUserTokenEntity> {
*
* @param userId 用户ID
*/
Result createToken(Long userId);
Result<TokenDTO> createToken(Long userId);
SysUserDTO getUserByToken(String token);
/**
* 退出
@@ -1,6 +1,9 @@
package xiaozhi.modules.security.service.impl;
import cn.hutool.core.date.DateUtil;
import lombok.AllArgsConstructor;
import xiaozhi.common.exception.ErrorCode;
import xiaozhi.common.exception.RenException;
import xiaozhi.common.page.TokenDTO;
import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.utils.HttpContextUtils;
@@ -10,18 +13,23 @@ import xiaozhi.modules.security.entity.SysUserTokenEntity;
import xiaozhi.modules.security.oauth2.TokenGenerator;
import xiaozhi.modules.security.service.SysUserTokenService;
import org.springframework.stereotype.Service;
import xiaozhi.modules.sys.dto.SysUserDTO;
import xiaozhi.modules.sys.service.SysUserService;
import java.util.Date;
@AllArgsConstructor
@Service
public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, SysUserTokenEntity> implements SysUserTokenService {
private final SysUserService sysUserService;
/**
* 12小时后过期
*/
private final static int EXPIRE = 3600 * 12;
@Override
public Result createToken(Long userId) {
public Result<TokenDTO> createToken(Long userId) {
//用户token
String token;
@@ -70,6 +78,20 @@ public class SysUserTokenServiceImpl extends BaseServiceImpl<SysUserTokenDao, Sy
return new Result().ok(tokenDTO);
}
@Override
public SysUserDTO getUserByToken(String token) {
SysUserTokenEntity userToken = baseDao.getByToken(token);
Date now = new Date();
if (userToken.getExpireDate().before(now)) {
throw new RenException(ErrorCode.UNAUTHORIZED);
}
SysUserDTO userDTO = sysUserService.getByUserId(userToken.getUserId());
userDTO.setPassword("");
return userDTO;
}
@Override
public void logout(Long userId) {
Date expireDate = DateUtil.offsetMinute(new Date(), -1);
@@ -12,6 +12,8 @@ public interface SysUserService extends BaseService<SysUserEntity> {
SysUserDTO getByUsername(String username);
SysUserDTO getByUserId(Long userId);
void save(SysUserDTO dto);
void delete(Long[] ids);
@@ -41,6 +41,13 @@ public class SysUserServiceImpl extends BaseServiceImpl<SysUserDao, SysUserEntit
return ConvertUtils.sourceToTarget(entity, SysUserDTO.class);
}
@Override
public SysUserDTO getByUserId(Long userId) {
SysUserEntity sysUserEntity = sysUserDao.selectById(userId);
return ConvertUtils.sourceToTarget(sysUserEntity, SysUserDTO.class);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void save(SysUserDTO dto) {