1.修改1101(求职者)、1102(招聘者)、1103(网格员)、1104(内部工作者)
2.添加门户认证的类
This commit is contained in:
@@ -111,7 +111,7 @@ public class SecurityConfig
|
||||
.authorizeHttpRequests((requests) -> {
|
||||
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
|
||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
||||
requests.antMatchers("/login", "/register", "/captchaImage","/app/login","/websocket/**","/speech-recognition","/speech-synthesis","/cms/company/listPage","/cms/appUser/noTmlist").permitAll()
|
||||
requests.antMatchers("/login", "/register", "/captchaImage","/app/login","/websocket/**","/speech-recognition","/speech-synthesis","/cms/company/listPage","/cms/appUser/noTmlist","/getTjmhToken").permitAll()
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
// 移动端公用查询,可匿名访问
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
package com.ruoyi.framework.web.service;
|
||||
|
||||
import com.ruoyi.cms.util.oauth.OauthClient;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.domain.entity.SysUser;
|
||||
import com.ruoyi.common.core.domain.entity.tymh.nwToken.NwTokenResult;
|
||||
import com.ruoyi.common.core.domain.entity.tymh.nwToken.NwUserInfoResult;
|
||||
import com.ruoyi.common.core.domain.entity.tymh.nwToken.PortalTokenCacheDTO;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.MessageUtils;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.ServletUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.ip.IpUtils;
|
||||
import com.ruoyi.framework.manager.AsyncManager;
|
||||
import com.ruoyi.framework.manager.factory.AsyncFactory;
|
||||
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Service
|
||||
public class OauthLoginService {
|
||||
|
||||
@Autowired
|
||||
private OauthClient oauthClient;
|
||||
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
@Autowired
|
||||
private ISysUserService sysUserService;
|
||||
@Autowired
|
||||
private AuthenticationManager authenticationManager;
|
||||
// Redis缓存:门户UserID → 若依本地用户名(避免重复匹配数据库)
|
||||
private static final String REDIS_KEY_PORTAL_USER_MAPPING = "portal:user:mapping:";
|
||||
// 门户 Token 存储前缀(Redis 键:门户 userId → 门户 Token 信息)
|
||||
private static final String REDIS_KEY_PORTAL_TOKEN = "portal:token:";
|
||||
|
||||
/**
|
||||
* OAuth 登录流程:通过授权码获取系统令牌
|
||||
* @param code 前端传入的 OAuth 授权码
|
||||
* @return 系统内部令牌(供前端后续使用)
|
||||
*/
|
||||
public String oauthLogin(String code) {
|
||||
try {
|
||||
//获取门户token
|
||||
NwTokenResult nwTokenResult = oauthClient.nwGetToken(code);
|
||||
if (!"0".equals(nwTokenResult.getErrflag())) {
|
||||
throw new ServiceException("获取门户 Token 失败:" + nwTokenResult.getErrtext());
|
||||
}
|
||||
String accessToken = nwTokenResult.getAccessToken();
|
||||
Long expiresIn = nwTokenResult.getExpiresIn();
|
||||
if (StringUtils.isEmpty(accessToken) || expiresIn == null || expiresIn <= 0) {
|
||||
throw new ServiceException("门户 Token 无效或有效期异常");
|
||||
}
|
||||
//获取门户userInfo
|
||||
NwUserInfoResult portalUser = oauthClient.nwGetUserInfo(accessToken);
|
||||
//匹配/创建本地用户
|
||||
String localUsername = getOrCreateLocalUser(portalUser);
|
||||
//执行原来的登录流程
|
||||
Authentication authentication = authenticateLocalUser(localUsername);
|
||||
AsyncManager.me().execute(AsyncFactory.recordLogininfor(localUsername, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
|
||||
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
|
||||
|
||||
storePortalToken(loginUser.getUserId(), nwTokenResult);
|
||||
recordLoginInfo(loginUser.getUserId());
|
||||
return tokenService.createToken(loginUser);
|
||||
} catch (IOException | TimeoutException e) {
|
||||
throw new ServiceException("OAuth 登录失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配/创建本地用户,返回若依本地用户名
|
||||
*/
|
||||
private String getOrCreateLocalUser(NwUserInfoResult portalUser) {
|
||||
Long portalUserId = parsePortalUserId(portalUser.getUserid());
|
||||
|
||||
// 先从Redis查询缓存的本地用户名
|
||||
String cacheKey = REDIS_KEY_PORTAL_USER_MAPPING + portalUserId;
|
||||
String localUsername = redisCache.getCacheObject(cacheKey);
|
||||
if (StringUtils.isNotBlank(localUsername)) {
|
||||
return localUsername;
|
||||
}
|
||||
|
||||
// 缓存未命中,查询本地用户
|
||||
SysUser localUser = sysUserService.selectUserById(portalUserId);
|
||||
|
||||
if (localUser == null) {
|
||||
// 本地无用户,自动创建
|
||||
localUser = createLocalUser(portalUser, portalUserId);
|
||||
// 缓存门户UserID与本地用户名的映射(有效期1天,可调整)
|
||||
redisCache.setCacheObject(cacheKey, localUser.getUserName(), 1, TimeUnit.DAYS);
|
||||
return localUser.getUserName();
|
||||
}
|
||||
|
||||
// 缓存映射关系(更新有效期)
|
||||
redisCache.setCacheObject(cacheKey, localUser.getUserName(), 1, TimeUnit.DAYS);
|
||||
return localUser.getUserName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 门户UserID字符串转Long
|
||||
*/
|
||||
private Long parsePortalUserId(String portalUserIdStr) {
|
||||
try {
|
||||
return Long.parseLong(portalUserIdStr);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ServiceException("门户用户ID格式错误:" + portalUserIdStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动创建本地用户
|
||||
*/
|
||||
private SysUser createLocalUser(NwUserInfoResult portalUser, Long portalUserId) {
|
||||
SysUser newUser = new SysUser();
|
||||
String localUsername = "portal_" + portalUserId;
|
||||
newUser.setUserName(localUsername);
|
||||
newUser.setNickName(portalUser.getName());
|
||||
newUser.setIdCard(portalUser.getIdcardno());
|
||||
newUser.setUserId(portalUserId);
|
||||
newUser.setPassword(SecurityUtils.encryptPassword("123456"));
|
||||
newUser.setDelFlag("0");
|
||||
|
||||
// 调用若依原生方法新增用户(自动处理角色关联,需提前配置默认角色)
|
||||
sysUserService.insertUser(newUser);
|
||||
return newUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复用若依认证机制,触发 UserDetailsService 加载 LoginUser
|
||||
* (关键:用本地用户名构建认证令牌,无需密码,因为门户已完成身份校验)
|
||||
*/
|
||||
private Authentication authenticateLocalUser(String localUsername) {
|
||||
Authentication authentication = null;
|
||||
try {
|
||||
// 构建认证令牌:用户名+空密码(因为门户已验证身份,本地仅需加载用户信息)
|
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(localUsername, "");
|
||||
AuthenticationContextHolder.setContext(authenticationToken);
|
||||
|
||||
// 触发认证:会调用 UserDetailsServiceImpl.loadUserByUsername(localUsername)
|
||||
// 该方法会加载用户权限、角色,返回 LoginUser
|
||||
authentication = authenticationManager.authenticate(authenticationToken);
|
||||
} catch (Exception e) {
|
||||
// 捕获认证异常(如用户被禁用、权限加载失败等)
|
||||
throw new ServiceException("本地用户认证失败:" + e.getMessage());
|
||||
} finally {
|
||||
AuthenticationContextHolder.clearContext();
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储门户 Token 到 Redis(结构化存储,含过期时间)
|
||||
*/
|
||||
private void storePortalToken(Long portalUserId, NwTokenResult tokenResult) {
|
||||
// 构建 Redis 键(门户 userId 作为唯一标识)
|
||||
String redisKey = REDIS_KEY_PORTAL_TOKEN + portalUserId;
|
||||
|
||||
// 封装 Token 信息(含 accessToken、过期时间戳)
|
||||
PortalTokenCacheDTO tokenCache = new PortalTokenCacheDTO();
|
||||
tokenCache.setAccessToken(tokenResult.getAccessToken());
|
||||
tokenCache.setExpireTimestamp(System.currentTimeMillis() + tokenResult.getExpiresIn() * 1000);
|
||||
|
||||
redisCache.setCacheObject(redisKey, tokenCache, safeLongToInt(tokenResult.getExpiresIn()), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录登录信息(复用若依原生逻辑,直接复制过来)
|
||||
*/
|
||||
private void recordLoginInfo(Long userId) {
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserId(userId);
|
||||
sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
|
||||
sysUser.setLoginDate(new Date());
|
||||
sysUserService.updateUserProfile(sysUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Long 转 int
|
||||
*/
|
||||
private int safeLongToInt(Long value) {
|
||||
if (value == null) {
|
||||
throw new ServiceException("门户 Token 有效期不能为空");
|
||||
}
|
||||
// 校验是否超过 int 最大值(实际场景几乎不会触发)
|
||||
if (value > Integer.MAX_VALUE) {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
// 校验是否为负数(无效有效期)
|
||||
if (value < 0) {
|
||||
throw new ServiceException("门户 Token 有效期不能为负数");
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user