添加互联网端,单点登录基础类

This commit is contained in:
sh
2025-11-16 18:32:13 +08:00
parent 2a866ce0e6
commit cc37ece461
12 changed files with 394 additions and 5 deletions

View File

@@ -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","/getTjmhToken").permitAll()
requests.antMatchers("/login", "/register", "/captchaImage","/app/login","/websocket/**","/speech-recognition","/speech-synthesis","/cms/company/listPage","/cms/appUser/noTmlist","/getTjmhToken","/getWwTjmhToken").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
// 移动端公用查询,可匿名访问

View File

@@ -0,0 +1,199 @@
package com.ruoyi.framework.web.service;
import com.ruoyi.cms.util.StringUtil;
import com.ruoyi.cms.util.oauth.OauthClient;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.CompanyContact;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.tymh.nwToken.PortalTokenCacheDTO;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwTokenResult;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwTyInfo;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwUserLogin;
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.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@Service
public class OauthLoginHlwService {
@Autowired
private OauthClient oauthClient;
@Autowired
private TokenService tokenService;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysUserService sysUserService;
@Autowired
private UserDetailsService userDetailsService;
// Redis缓存门户UserID → 若依本地用户名(避免重复匹配数据库)
private static final String REDIS_KEY_PORTAL_USER_MAPPING = "hlw:user:mapping:";
// 门户 Token 存储前缀Redis 键:门户 userId → 门户 Token 信息)
private static final String REDIS_KEY_PORTAL_TOKEN = "hlw:token:";
private static final String USER_KEY="hlw_";
final private int expireTime=30;
protected static final long MILLIS_SECOND = 1000;
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
/**
* OAuth 登录流程:通过授权码获取系统令牌
* @return 系统内部令牌(供前端后续使用)
*/
public String getWwTjmhToken(WwUserLogin wwUserLogin){
try {
//获取门户token
WwTokenResult wwTokenResult = oauthClient.wwGetToken(wwUserLogin);
String wwToken=wwTokenResult.getAccessToken();
System.out.println("wwToken======================="+wwToken);
if (StringUtils.isBlank(wwToken)) {
throw new ServiceException("获取门户 Token 失败:" + wwToken);
}
//获取门户userInfo
WwTyInfo portalUser = oauthClient.wwGetUserInfo(wwToken);
//匹配/创建本地用户
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(), wwToken);
recordLoginInfo(loginUser.getUserId());
return tokenService.createToken(loginUser);
} catch (Exception e) {
throw new ServiceException("OAuth 登录失败:" + e.getMessage());
}
}
/**
* 匹配/创建本地用户,返回若依本地用户名
*/
private String getOrCreateLocalUser(WwTyInfo wwTyInfo) {
SysUser localUser=sysUserService.selectUserByIdCard(wwTyInfo.getIdno());
// 先从Redis查询缓存的本地用户名
String cacheKey = REDIS_KEY_PORTAL_USER_MAPPING + localUser.getUserId();
String localUsername = redisCache.getCacheObject(cacheKey);
if (StringUtils.isNotBlank(localUsername)) {
return localUsername;
}
if (localUser == null) {
// 本地无用户,自动创建
localUser = createLocalUser(wwTyInfo);
// 缓存门户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 parseStringToLoing(String portalUserIdStr) {
try {
return Long.parseLong(portalUserIdStr);
} catch (NumberFormatException e) {
throw new ServiceException("门户用户ID格式错误" + portalUserIdStr);
}
}
/**
* 自动创建本地用户
*/
private SysUser createLocalUser(WwTyInfo wwTyInfo) {
SysUser newUser = new SysUser();
switch (wwTyInfo.getUsertype()) {
case "1"://个人
newUser.setNickName(wwTyInfo.getName());
newUser.setIdCard(wwTyInfo.getIdno());
newUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QZZ)});
newUser.setUserName(wwTyInfo.getName());
break;
default://单位
newUser.setNickName(wwTyInfo.getEnterprisename());
newUser.setIdCard(wwTyInfo.getEnterprisecode());
newUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QY)});
newUser.setUserName(wwTyInfo.getEnterprisename());
//企业联系人
CompanyContact companyContact=new CompanyContact();
companyContact.setContactPerson(wwTyInfo.getContactperson());
companyContact.setContactPersonPhone(wwTyInfo.getContactphone());
}
newUser.setPassword(SecurityUtils.encryptPassword("123456"));
newUser.setDelFlag("0");
sysUserService.insertUser(newUser);
return newUser;
}
/**
* 复用若依认证机制,触发 UserDetailsService 加载 LoginUser
* (关键:用本地用户名构建认证令牌,无需密码,因为门户已完成身份校验)
*/
private Authentication authenticateLocalUser(String localUsername) {
Authentication authentication = null;
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(localUsername);
authentication = new UsernamePasswordAuthenticationToken(
userDetails,
null, // 密码为 null彻底绕过 Spring Security 的密码校验
userDetails.getAuthorities()
);
AuthenticationContextHolder.setContext(authentication);
} catch (Exception e) {
throw new ServiceException("本地用户认证失败:" + e.getMessage());
} finally {
AuthenticationContextHolder.clearContext();
}
return authentication;
}
/**
* 存储门户 Token 到 Redis结构化存储含过期时间
*/
private void storePortalToken(Long portalUserId, String accessToken) {
String redisKey = REDIS_KEY_PORTAL_TOKEN + portalUserId;
PortalTokenCacheDTO tokenCache = new PortalTokenCacheDTO();
tokenCache.setAccessToken(accessToken);
tokenCache.setExpireTimestamp(System.currentTimeMillis() + expireTime * MILLIS_MINUTE);
redisCache.setCacheObject(redisKey, tokenCache, expireTime, 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);
}
}