app权限适配

This commit is contained in:
2025-06-23 18:34:30 +08:00
parent 3278b789dd
commit fb94eaf97c
14 changed files with 205 additions and 46 deletions

View File

@ -17,6 +17,7 @@ package com.fuyuanshen.config;
import com.fuyuanshen.utils.SecurityUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;

View File

@ -16,6 +16,7 @@
package com.fuyuanshen.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.jwt.Claims;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson2.JSON;

View File

@ -55,6 +55,11 @@ public class SecurityProperties {
*/
private String onlineKey;
/**
* app在线用户
*/
private String appOnlineKey;
/**
* 验证码 key
*/

View File

@ -17,6 +17,7 @@ package com.fuyuanshen.modules.security.rest;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.MD5;
import com.fuyuanshen.annotation.Log;
import com.fuyuanshen.annotation.rest.AnonymousDeleteMapping;
import com.fuyuanshen.annotation.rest.AnonymousGetMapping;
@ -31,6 +32,7 @@ import com.fuyuanshen.modules.security.security.app.AppTokenProvider;
import com.fuyuanshen.modules.security.service.OnlineUserService;
import com.fuyuanshen.modules.security.service.UserDetailsServiceImpl;
import com.fuyuanshen.modules.security.service.dto.app.AppAuthUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppRoleDto;
import com.fuyuanshen.modules.security.service.dto.AuthUserDto;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
@ -55,6 +57,7 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -89,7 +92,6 @@ public class AuthController {
private final UserDetailsServiceImpl userDetailsService;
private final APPUserMapper appUserMapper;
private final APPUserService appUserService;
@Log("用户登录")
@ApiOperation("登录授权")
@AnonymousPostMapping(value = "/login")
@ -151,7 +153,6 @@ public class AuthController {
@AnonymousPostMapping(value = "/app/login")
public ResponseEntity<Object> APPLogin(@Validated @RequestBody AppAuthUserDto authUser, HttpServletRequest request) throws Exception {
// 1. 构建查询参数
APPUserQuery appUserQuery = new APPUserQuery();
appUserQuery.setPhoneNumber(authUser.getPhoneNumber());
@ -168,20 +169,22 @@ public class AuthController {
}
// 3. 验证密码
if (!appUser.getPassword().equals(authUser.getPassword())) {
// String enPassword = passwordEncoder.encode(authUser.getPassword());
String enPassword = MD5.create().digestHex(authUser.getPassword());
if (!appUser.getPassword().equals(enPassword)) {
throw new BadRequestException("登录密码错误");
}
// 4. 加载用户详情
JwtUserDto jwtUser = userDetailsService.loadUserByUsername(appUser.getUsername(),appUser.getUserType());
JwtUserDto jwtUser = userDetailsService.loadAppUserByUsername(appUser.getUsername());
// 5. 创建认证信息
Authentication authentication = new UsernamePasswordAuthenticationToken(jwtUser, null, jwtUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
// 6. 生成 Token
String token = appTokenProvider.createToken(jwtUser);
String token = appTokenProvider.createAppToken(jwtUser);
// 7. 获取角色权限
//Integer optLevel = appUserService.selectRoleByUserLevel(appUser.getRoles());
@ -198,7 +201,7 @@ public class AuthController {
}
// 10. 记录在线状态
onlineUserService.save(jwtUser, token, request);
onlineUserService.saveAppOnlineUser(jwtUser, token, request);
// 11. 返回结果
return ResponseEntity.ok(authInfo);

View File

@ -75,9 +75,10 @@ public class TokenProvider implements InitializingBean {
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
claims.put("userType","0");//0 系统登录 1 APP登录
return jwtBuilder
.setClaims(claims)
.setSubject(user.getUsername())
.setSubject(user.getUser().getUsername())
.compact();
}
@ -134,6 +135,16 @@ public class TokenProvider implements InitializingBean {
return properties.getOnlineKey() + claims.getSubject() + ":" + getId(token);
}
/**
* 获取app登录用户RedisKey
* @param token /
* @return key
*/
public String appLoginKey(String token) {
Claims claims = getClaims(token);
return properties.getAppOnlineKey() + claims.getSubject() + ":" + getId(token);
}
/**
* 获取会话编号
* @param token /

View File

@ -20,6 +20,7 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.fuyuanshen.modules.security.config.SecurityProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
@ -90,6 +91,32 @@ public class AppTokenProvider implements InitializingBean {
.compact();
}
/**
* APP创建Token 设置永不过期,
* Token 的时间有效性转到Redis 维护
* @param user /
* @return /
*/
public String createAppToken(JwtUserDto user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// if (user.getAppUser() != null){
// claims.put(AUTHORITIES_UID_KEY, user.getAppUser().getId());
// }else {
// claims.put(AUTHORITIES_UID_KEY, 0);
// }
// 设置UUID确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
claims.put("userType","1");//0 系统登录 1 APP登录
return jwtBuilder
.setClaims(claims)
.setSubject(user.getAppUser().getUsername())
.compact();
}
/**
* 依据Token 获取鉴权信息
*

View File

@ -15,6 +15,7 @@
*/
package com.fuyuanshen.modules.security.service;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.modules.security.security.TokenProvider;
@ -67,6 +68,28 @@ public class OnlineUserService {
redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
/**
* 保存在线用户信息
* @param jwtUserDto /
* @param token /
* @param request /
*/
public void saveAppOnlineUser(JwtUserDto jwtUserDto, String token, HttpServletRequest request){
String dept = jwtUserDto.getAppUser().getDept() == null ? null : jwtUserDto.getAppUser().getDept().getName();
String ip = StringUtils.getIp(request);
String id = tokenProvider.getId(token);
String browser = StringUtils.getBrowser(request);
String address = StringUtils.getCityInfo(ip);
OnlineUserDto onlineUserDto = null;
try {
onlineUserDto = new OnlineUserDto(id, jwtUserDto.getAppUser().getUsername(), jwtUserDto.getAppUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date());
} catch (Exception e) {
log.error(e.getMessage(),e);
}
String loginKey = tokenProvider.loginKey(token);
redisUtils.set(loginKey, onlineUserDto, properties.getTokenValidityInSeconds(), TimeUnit.MILLISECONDS);
}
/**
* 查询全部数据

View File

@ -18,6 +18,7 @@ package com.fuyuanshen.modules.security.service;
import cn.hutool.core.util.RandomUtil;
import com.fuyuanshen.modules.security.config.LoginProperties;
import com.fuyuanshen.modules.security.service.dto.JwtUserDto;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.utils.RedisUtils;
import com.fuyuanshen.utils.StringUtils;
import org.springframework.beans.factory.annotation.Value;
@ -53,9 +54,26 @@ public class UserCacheManager {
// 获取数据
return redisUtils.get(LoginProperties.cacheKey + userName, JwtUserDto.class);
}
return null;
}
/**
* 返回用户缓存
*
* @param userName 用户名
* @return JwtUserDto
*/
public JwtUserDto getAppUserCache(String userName) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
return redisUtils.get(LoginProperties.cacheKey_app + userName, JwtUserDto.class);
}
return null;
}
/**
* 添加缓存到Redis
@ -95,13 +113,13 @@ public class UserCacheManager {
* @param userName 用户名
* @return JwtUserDto
*/
public JwtUserDto getUserCache(String userName, Integer userType) {
public AppJwtUserDto getUserCache(String userName, Integer userType) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 获取数据
try {
JwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey_app + userName, JwtUserDto.class);
AppJwtUserDto jwtUserDto = redisUtils.get(LoginProperties.cacheKey_app + userName, AppJwtUserDto.class);
if (jwtUserDto != null) {
jwtUserDto.getUsername();
}
@ -132,6 +150,22 @@ public class UserCacheManager {
}
}
/**
* App用户添加缓存到Redis
*
* @param userName 用户名
*/
@Async
public void addAppUserCache(String userName, JwtUserDto user) {
// 转小写
userName = StringUtils.lowerCase(userName);
if (StringUtils.isNotEmpty(userName)) {
// 添加数据, 避免数据同时过期
long time = idleTime + RandomUtil.randomInt(900, 1800);
redisUtils.set(LoginProperties.cacheKey_app + userName, user, time);
}
}
/**
* 清理用户缓存信息
* 用户信息变更时

View File

@ -15,7 +15,11 @@
*/
package com.fuyuanshen.modules.security.service;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.fuyuanshen.modules.security.service.dto.app.AppJwtUserDto;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import com.fuyuanshen.utils.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.fuyuanshen.exception.BadRequestException;
@ -47,31 +51,55 @@ public class UserDetailsServiceImpl implements UserDetailsService {
@Override
public JwtUserDto loadUserByUsername(String username) {
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
if (jwtUserDto == null) {
User user = userService.getLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活!");
JWT jwt = JWTUtil.parseToken(SecurityUtils.getToken());
String userType = jwt.getPayload("userType").toString();
if("1".equals(userType)){
JwtUserDto jwtUserDto = userCacheManager.getAppUserCache(username);
if (jwtUserDto == null) {
APPUser user = userService.appGetLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活!");
}
// 获取用户的权限
List<AuthorityDto> authorities = roleService.appBuildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(user,null, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addAppUserCache(username, jwtUserDto);
}
// 获取用户的权限
List<AuthorityDto> authorities = roleService.buildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(user, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
}
return jwtUserDto;
}else{
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
if (jwtUserDto == null) {
User user = userService.getLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
} else {
if (!user.getEnabled()) {
throw new BadRequestException("账号未激活!");
}
// 获取用户的权限
List<AuthorityDto> authorities = roleService.buildPermissions(user);
// 初始化JwtUserDto
jwtUserDto = new JwtUserDto(null,user, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto);
}
}
return jwtUserDto;
}
return jwtUserDto;
}
public JwtUserDto loadAppUserByUsername(String username) {
public JwtUserDto loadUserByUsername(String username, Integer userType) {
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username , userType);
JwtUserDto jwtUserDto = userCacheManager.getUserCache(username);
if (jwtUserDto == null) {
username = username.replace("APP_", "");
APPUser user = userService.appGetLoginData(username);
if (user == null) {
throw new BadRequestException("用户不存在");
@ -82,11 +110,17 @@ public class UserDetailsServiceImpl implements UserDetailsService {
// 获取用户的权限
List<AuthorityDto> authorities = roleService.appBuildPermissions(user);
// 初始化JwtUserDto
// jwtUserDto = new JwtUserDto(null,user, dataService.getDeptIds(user), authorities);
jwtUserDto = new JwtUserDto(user,null, dataService.getDeptIds(user), authorities);
// 添加缓存数据
userCacheManager.addUserCache(username, jwtUserDto, userType);
userCacheManager.addAppUserCache(username, jwtUserDto);
}
}
return jwtUserDto;
}
private boolean isAppUser(String username) {
// 实现你的判断逻辑,比如前缀、数据库查询等
return username.startsWith("APP_");
}
}

View File

@ -16,6 +16,7 @@
package com.fuyuanshen.modules.security.service.dto;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fuyuanshen.modules.system.domain.app.APPUser;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -34,6 +35,8 @@ import java.util.stream.Collectors;
@Getter
@AllArgsConstructor
public class JwtUserDto implements UserDetails {
@ApiModelProperty(value = "app用户")
private final APPUser appUser;
@ApiModelProperty(value = "用户")
private final User user;
@ -51,12 +54,19 @@ public class JwtUserDto implements UserDetails {
@Override
@JSONField(serialize = false)
public String getPassword() {
if (appUser != null) {
return appUser.getPassword();
}
return user.getPassword();
}
@Override
@JSONField(serialize = false)
public String getUsername() {
if (appUser != null) {
return appUser.getUsername();
}
return user.getUsername();
}
@ -81,6 +91,9 @@ public class JwtUserDto implements UserDetails {
@Override
@JSONField(serialize = false)
public boolean isEnabled() {
if (appUser != null) {
return appUser.getEnabled();
}
return user.getEnabled();
}
}

View File

@ -271,8 +271,8 @@ public class UserController {
return ResponseVO.success(null);
}
@Log("修改用户:个人中心")
@ApiOperation("修改用户:个人中心")
@Log("修改用户:")
@ApiOperation("修改用户:")
@PutMapping(value = "center")
public ResponseVO<Object> centerUser(@Validated(User.Update.class) @RequestBody User resources) {
if (!resources.getId().equals(SecurityUtils.getCurrentUserId())) {

View File

@ -87,7 +87,7 @@ public class APPUserController {
return ResponseVO.success(appUserService.queryAPPUser(criteria, page));
}
@Log("app用户注册")
@Log("app")
@ApiOperation("app用户注册")
@AnonymousPostMapping(value = "/app/register")
public ResponseVO<String> APPRegister(@Validated @RequestBody APPUserDTO user) throws Exception {

View File

@ -15,6 +15,7 @@
*/
package com.fuyuanshen.modules.system.service.impl.app;
import cn.hutool.crypto.digest.MD5;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fuyuanshen.exception.BadRequestException;
@ -27,6 +28,7 @@ import com.fuyuanshen.modules.system.service.app.APPUserService;
import com.fuyuanshen.modules.utils.ResponseVO;
import com.fuyuanshen.utils.*;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Set;
@ -41,7 +43,7 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
private final APPUserMapper appUserMapper;
private final PasswordEncoder passwordEncoder;
/**
* 查询APP/小程序用户
@ -67,24 +69,27 @@ public class APPUserServiceImpl extends ServiceImpl<APPUserMapper, APPUser> impl
@Override
public ResponseVO<Object> addUser(APPUserDTO user) {
String username = user.getPhoneNumber().toString();
String username = user.getPhoneNumber();
if (appUserMapper.getByUsername(username) != null) {
throw new BadRequestException("该手机号已被注册");
}
APPUser appUser = new APPUser();
appUser.setUsername(user.getPhoneNumber().toString());
appUser.setPassword(user.getPassword());
appUser.setNickName(user.getPhoneNumber());
appUser.setUserLevel((byte) 1);
appUser.setPhone(Long.valueOf(user.getPhoneNumber()));
appUser.setAdmin((byte) 1);
appUser.setEnabled(true);
appUser.setUserType(0);
System.out.println("--------------------"+appUser);
APPUser appUser = new APPUser();
appUser.setUsername(user.getPhoneNumber());
// String enPassword = passwordEncoder.encode(user.getPassword());
String enPassword = MD5.create().digestHex(user.getPassword());
appUser.setPassword(enPassword);
appUser.setNickName(user.getPhoneNumber());
appUser.setUserLevel((byte) 1);
appUser.setPhone(Long.valueOf(user.getPhoneNumber()));
appUser.setAdmin((byte) 1);
appUser.setEnabled(true);
appUser.setUserType(0);
System.out.println("--------------------"+appUser);
appUserMapper.insert(appUser);
return ResponseVO.success("注册成功");
appUserMapper.insert(appUser);
return ResponseVO.success("注册成功");
}

View File

@ -107,6 +107,8 @@ jwt:
token-validity-in-seconds: 14400000
# 在线用户key
online-key: "online_token:"
# app在线用户key
app_online-key: "app_online_token:"
# 验证码
code-key: "captcha_code:"
# token 续期检查时间范围默认30分钟单位毫秒在token即将过期的一段时间内用户操作了则给用户的token续期