mirror of https://gitee.com/stylefeng/roses
【7.0.4】【auth】增加loginWithToken接口
parent
94b59af0da
commit
bb99b7f523
|
@ -27,6 +27,7 @@ package cn.stylefeng.roses.kernel.auth.api;
|
|||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest;
|
||||
|
||||
/**
|
||||
* 认证服务的接口,包括基本的登录退出操作和校验token等操作
|
||||
|
@ -55,6 +56,25 @@ public interface AuthServiceApi {
|
|||
*/
|
||||
LoginResponse loginWithUserName(String username);
|
||||
|
||||
/**
|
||||
* 登录(通过账号和sso后的token),一般用在单点登录
|
||||
*
|
||||
* @param username 账号
|
||||
* @param caToken sso登录成功后的会话
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:44
|
||||
*/
|
||||
LoginResponse loginWithUserNameAndCaToken(String username, String caToken);
|
||||
|
||||
/**
|
||||
* 通过token进行登录,一般用在单点登录服务
|
||||
*
|
||||
* @param loginWithTokenRequest 请求
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:44
|
||||
*/
|
||||
LoginResponse LoginWithToken(LoginWithTokenRequest loginWithTokenRequest);
|
||||
|
||||
/**
|
||||
* 当前登录人退出登录
|
||||
*
|
||||
|
|
|
@ -77,4 +77,49 @@ public interface AuthConstants {
|
|||
*/
|
||||
String KAPTCHA_SESSION_KEY = "KAPTCHA_SESSION_KEY";
|
||||
|
||||
/**
|
||||
* 默认解析jwt的秘钥(用于解析sso传过来的token)
|
||||
*/
|
||||
String SYS_AUTH_SSO_JWT_SECRET = "aabbccdd";
|
||||
|
||||
/**
|
||||
* 默认解密sso单点中jwt中payload的秘钥
|
||||
*/
|
||||
String SYS_AUTH_SSO_DECRYPT_DATA_SECRET = "EDPpR/BQfEFJiXKgxN8Uno4OnNMGcIJW1F777yySCPA=";
|
||||
|
||||
/**
|
||||
* 是否开启sso远程会话校验
|
||||
*/
|
||||
Boolean SYS_AUTH_SSO_SESSION_VALIDATE_SWITCH = false;
|
||||
|
||||
/**
|
||||
* 用于远程session校验redis的host
|
||||
*/
|
||||
String SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_HOST = "localhost";
|
||||
|
||||
/**
|
||||
* 用于远程session校验redis的端口
|
||||
*/
|
||||
Integer SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_PORT = 6379;
|
||||
|
||||
/**
|
||||
* 用于远程session校验redis的数据库index
|
||||
*/
|
||||
Integer SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_DB_INDEX = 2;
|
||||
|
||||
/**
|
||||
* 用于远程session校验redis的缓存前缀
|
||||
*/
|
||||
String SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_CACHE_PREFIX = "CA:USER:TOKEN:";
|
||||
|
||||
/**
|
||||
* SSO的默认地址
|
||||
*/
|
||||
String SYS_AUTH_SSO_HOST = "http://localhost:8888";
|
||||
|
||||
/**
|
||||
* sso获取loginCode的url
|
||||
*/
|
||||
String SYS_AUTH_SSO_GET_LOGIN_CODE = "/sso/getLoginCode";
|
||||
|
||||
}
|
||||
|
|
|
@ -96,7 +96,27 @@ public enum AuthExceptionEnum implements AbstractExceptionEnum {
|
|||
/**
|
||||
* 用户角色未绑定,登录失败
|
||||
*/
|
||||
ROLE_IS_EMPTY(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "12", "用户角色未绑定,登录失败");
|
||||
ROLE_IS_EMPTY(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "12", "用户角色未绑定,登录失败"),
|
||||
|
||||
/**
|
||||
* SSO登录获取loginCode失败
|
||||
*/
|
||||
SSO_LOGIN_CODE_GET_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "13", "登录失败,具体信息为:{}"),
|
||||
|
||||
/**
|
||||
* SSO使用token登录时,token解析异常
|
||||
*/
|
||||
SSO_TOKEN_PARSE_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "14", "token解析异常:{}"),
|
||||
|
||||
/**
|
||||
* SSO使用token登录时,解析token中的用户信息错误,用户信息为空
|
||||
*/
|
||||
SSO_TOKEN_GET_USER_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "15", "解析token中的用户信息错误,用户信息为空"),
|
||||
|
||||
/**
|
||||
* SSO使用token登录时,解密token出错
|
||||
*/
|
||||
SSO_TOKEN_DECRYPT_USER_ERROR(RuleConstants.BUSINESS_ERROR_TYPE_CODE + AuthConstants.AUTH_EXCEPTION_STEP_CODE + "16", "解密token出错:{}");
|
||||
|
||||
/**
|
||||
* 错误编码
|
||||
|
|
|
@ -144,4 +144,95 @@ public class AuthConfigExpander {
|
|||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_SESSION_COOKIE_NAME", String.class, DEFAULT_AUTH_HEADER_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认解析jwt的秘钥(用于解析sso传过来的token)
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoJwtSecret() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_JWT_SECRET", String.class, SYS_AUTH_SSO_JWT_SECRET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认解析sso加密的数据的秘钥
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoDataDecryptSecret() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_DECRYPT_DATA_SECRET", String.class, SYS_AUTH_SSO_DECRYPT_DATA_SECRET);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否开启sso远程会话校验,当系统对接sso后,如需同时校验sso的会话是否存在则开启此开关
|
||||
*
|
||||
* @return true-开启远程校验,false-关闭远程校验
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static Boolean getSsoSessionValidateSwitch() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_SESSION_VALIDATE_SWITCH", Boolean.class, SYS_AUTH_SSO_SESSION_VALIDATE_SWITCH);
|
||||
}
|
||||
|
||||
/**
|
||||
* sso会话远程校验,redis的host
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoSessionValidateRedisHost() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_HOST", String.class, SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_HOST);
|
||||
}
|
||||
|
||||
/**
|
||||
* sso会话远程校验,redis的端口
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static Integer getSsoSessionValidateRedisPort() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_PORT", Integer.class, SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* sso会话远程校验,redis的密码
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoSessionValidateRedisPassword() {
|
||||
return ConfigContext.me().getConfigValueNullable("SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_PASSWORD", String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* sso会话远程校验,redis的db
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static Integer getSsoSessionValidateRedisDbIndex() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_DB_INDEX", Integer.class, SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_DB_INDEX);
|
||||
}
|
||||
|
||||
/**
|
||||
* sso会话远程校验,redis的缓存前缀
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoSessionValidateRedisCachePrefix() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_CACHE_PREFIX", String.class, SYS_AUTH_SSO_SESSION_VALIDATE_REDIS_CACHE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SSO服务器的地址
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:39
|
||||
*/
|
||||
public static String getSsoUrl() {
|
||||
return ConfigContext.me().getSysConfigValueWithDefault("SYS_AUTH_SSO_HOST", String.class, SYS_AUTH_SSO_HOST);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.pojo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* SSO的配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:28
|
||||
*/
|
||||
@Data
|
||||
public class SsoProperties {
|
||||
|
||||
/**
|
||||
* 是否开启,true-开启单点,false-关闭单点
|
||||
*/
|
||||
private Boolean openFlag;
|
||||
|
||||
}
|
|
@ -51,10 +51,37 @@ public class LoginResponse {
|
|||
*/
|
||||
private Long expireAt;
|
||||
|
||||
/**
|
||||
* 使用单点登录
|
||||
*/
|
||||
private Boolean ssoLogin;
|
||||
|
||||
/**
|
||||
* 单点登录的loginCode
|
||||
*/
|
||||
private String ssoLoginCode;
|
||||
|
||||
/**
|
||||
* 用于普通登录的组装
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:31
|
||||
*/
|
||||
public LoginResponse(LoginUser loginUser, String token, Long expireAt) {
|
||||
this.loginUser = loginUser;
|
||||
this.token = token;
|
||||
this.expireAt = expireAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于单点登录,返回用户loginCode
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:31
|
||||
*/
|
||||
public LoginResponse(String loginCode) {
|
||||
this.ssoLogin = true;
|
||||
this.ssoLoginCode = loginCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
package cn.stylefeng.roses.kernel.auth.api.pojo.auth;
|
||||
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.request.BaseRequest;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 单点获取到的token
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:43
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class LoginWithTokenRequest extends BaseRequest {
|
||||
|
||||
/**
|
||||
* 从单点服务获取到的token
|
||||
*/
|
||||
@NotBlank(message = "token不能为空")
|
||||
private String token;
|
||||
|
||||
}
|
|
@ -68,7 +68,7 @@
|
|||
<!--token用的jwt token-->
|
||||
<dependency>
|
||||
<groupId>cn.stylefeng.roses</groupId>
|
||||
<artifactId>jwt-api</artifactId>
|
||||
<artifactId>jwt-sdk</artifactId>
|
||||
<version>${roses.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -24,21 +24,33 @@
|
|||
*/
|
||||
package cn.stylefeng.roses.kernel.auth.auth;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.SessionManagerApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.constants.AuthConstants;
|
||||
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.AuthException;
|
||||
import cn.stylefeng.roses.kernel.auth.api.exception.enums.AuthExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.auth.api.expander.AuthConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.auth.api.password.PasswordStoredEncryptApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.password.PasswordTransferEncryptApi;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.SsoProperties;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import cn.stylefeng.roses.kernel.jwt.JwtTokenOperator;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.context.JwtContext;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.exception.JwtException;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.exception.enums.JwtExceptionEnum;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.pojo.config.JwtConfig;
|
||||
import cn.stylefeng.roses.kernel.jwt.api.pojo.payload.DefaultJwtPayload;
|
||||
import cn.stylefeng.roses.kernel.log.api.LoginLogServiceApi;
|
||||
import cn.stylefeng.roses.kernel.message.api.expander.WebSocketConfigExpander;
|
||||
|
@ -49,6 +61,9 @@ import cn.stylefeng.roses.kernel.system.api.enums.UserStatusEnum;
|
|||
import cn.stylefeng.roses.kernel.system.api.expander.SystemConfigExpander;
|
||||
import cn.stylefeng.roses.kernel.system.api.pojo.user.UserLoginInfoDTO;
|
||||
import cn.stylefeng.roses.kernel.validator.api.exception.enums.ValidatorExceptionEnum;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -89,16 +104,72 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
@Resource
|
||||
private CaptchaApi captchaApi;
|
||||
|
||||
@Resource
|
||||
private SsoProperties ssoProperties;
|
||||
|
||||
@Override
|
||||
public LoginResponse login(LoginRequest loginRequest) {
|
||||
return loginAction(loginRequest, true);
|
||||
return loginAction(loginRequest, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResponse loginWithUserName(String username) {
|
||||
LoginRequest loginRequest = new LoginRequest();
|
||||
loginRequest.setAccount(username);
|
||||
return loginAction(new LoginRequest(), false);
|
||||
return loginAction(new LoginRequest(), false, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResponse loginWithUserNameAndCaToken(String username, String caToken) {
|
||||
LoginRequest loginRequest = new LoginRequest();
|
||||
loginRequest.setAccount(username);
|
||||
return loginAction(loginRequest, false, caToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginResponse LoginWithToken(LoginWithTokenRequest loginWithTokenRequest) {
|
||||
|
||||
// 解析jwt token中的账号
|
||||
JwtConfig jwtConfig = new JwtConfig();
|
||||
jwtConfig.setJwtSecret(AuthConfigExpander.getSsoJwtSecret());
|
||||
jwtConfig.setExpiredSeconds(0L);
|
||||
|
||||
// jwt工具类初始化
|
||||
JwtTokenOperator jwtTokenOperator = new JwtTokenOperator(jwtConfig);
|
||||
|
||||
// 解析token中的用户信息
|
||||
Claims payload = null;
|
||||
try {
|
||||
payload = jwtTokenOperator.getJwtPayloadClaims(loginWithTokenRequest.getToken());
|
||||
} catch (Exception exception) {
|
||||
throw new AuthException(AuthExceptionEnum.SSO_TOKEN_PARSE_ERROR, exception.getMessage());
|
||||
}
|
||||
|
||||
// 获取到用户信息
|
||||
Object userInfoEncryptString = payload.get("userInfo");
|
||||
if (ObjectUtil.isEmpty(userInfoEncryptString)) {
|
||||
throw new AuthException(AuthExceptionEnum.SSO_TOKEN_GET_USER_ERROR);
|
||||
}
|
||||
|
||||
// 解密出用户账号和caToken(caToken用于校验用户是否在单点中心)
|
||||
String account = null;
|
||||
String caToken = null;
|
||||
try {
|
||||
AES aesUtil = SecureUtil.aes(Base64.decode(AuthConfigExpander.getSsoDataDecryptSecret()));
|
||||
String loginUserJson = aesUtil.decryptStr(userInfoEncryptString.toString(), CharsetUtil.CHARSET_UTF_8);
|
||||
JSONObject userInfoJsonObject = JSON.parseObject(loginUserJson);
|
||||
account = userInfoJsonObject.getString("account");
|
||||
caToken = userInfoJsonObject.getString("caToken");
|
||||
} catch (Exception exception) {
|
||||
throw new AuthException(AuthExceptionEnum.SSO_TOKEN_DECRYPT_USER_ERROR, exception.getMessage());
|
||||
}
|
||||
|
||||
// 账号为空,抛出异常
|
||||
if (account == null) {
|
||||
throw new AuthException(AuthExceptionEnum.SSO_TOKEN_DECRYPT_USER_ERROR);
|
||||
}
|
||||
|
||||
return loginWithUserNameAndCaToken(account, caToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,10 +233,11 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
*
|
||||
* @param loginRequest 登录参数
|
||||
* @param validatePassword 是否校验密码,true-校验密码,false-不会校验密码
|
||||
* @param caToken 单点登录后服务端的token,一般为32位uuid
|
||||
* @author fengshuonan
|
||||
* @date 2020/10/21 16:59
|
||||
*/
|
||||
private LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword) {
|
||||
private LoginResponse loginAction(LoginRequest loginRequest, Boolean validatePassword, String caToken) {
|
||||
|
||||
// 1.参数为空校验
|
||||
if (validatePassword) {
|
||||
|
@ -194,10 +266,17 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
// 3. 解密密码的密文
|
||||
// String decryptPassword = passwordTransferEncryptApi.decrypt(loginRequest.getPassword());
|
||||
|
||||
// 4. 获取用户密码的加密值和用户的状态
|
||||
// 4. 如果开启了单点登录,并且CaToken没有值,走单点登录,获取loginCode
|
||||
if (ssoProperties.getOpenFlag() && StrUtil.isEmpty(caToken)) {
|
||||
// 调用单点的接口获取loginCode,远程接口校验用户级密码正确性。
|
||||
String remoteLoginCode = getRemoteLoginCode(loginRequest);
|
||||
return new LoginResponse(remoteLoginCode);
|
||||
}
|
||||
|
||||
// 5. 获取用户密码的加密值和用户的状态
|
||||
UserLoginInfoDTO userValidateInfo = userServiceApi.getUserLoginInfo(loginRequest.getAccount());
|
||||
|
||||
// 5. 校验用户密码是否正确
|
||||
// 6. 校验用户密码是否正确
|
||||
if (validatePassword) {
|
||||
Boolean checkResult = passwordStoredEncryptApi.checkPassword(loginRequest.getPassword(), userValidateInfo.getUserPasswordHexed());
|
||||
if (!checkResult) {
|
||||
|
@ -205,42 +284,82 @@ public class AuthServiceImpl implements AuthServiceApi {
|
|||
}
|
||||
}
|
||||
|
||||
// 6. 校验用户是否异常(不是正常状态)
|
||||
// 7. 校验用户是否异常(不是正常状态)
|
||||
if (!UserStatusEnum.ENABLE.getCode().equals(userValidateInfo.getUserStatus())) {
|
||||
throw new AuthException(AuthExceptionEnum.USER_STATUS_ERROR, UserStatusEnum.getCodeMessage(userValidateInfo.getUserStatus()));
|
||||
}
|
||||
|
||||
// 7. 获取LoginUser,用于用户的缓存
|
||||
// 8. 获取LoginUser,用于用户的缓存
|
||||
LoginUser loginUser = userValidateInfo.getLoginUser();
|
||||
|
||||
// 8. 生成用户的token
|
||||
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(loginUser.getUserId(), loginUser.getAccount(), loginRequest.getRememberMe());
|
||||
// 9. 生成用户的token
|
||||
DefaultJwtPayload defaultJwtPayload = new DefaultJwtPayload(loginUser.getUserId(), loginUser.getAccount(), loginRequest.getRememberMe(), caToken);
|
||||
String jwtToken = JwtContext.me().generateTokenDefaultPayload(defaultJwtPayload);
|
||||
loginUser.setToken(jwtToken);
|
||||
|
||||
synchronized (SESSION_OPERATE_LOCK) {
|
||||
|
||||
// 8.1 获取ws-url 保存到用户信息中
|
||||
// 9.1 获取ws-url 保存到用户信息中
|
||||
loginUser.setWsUrl(WebSocketConfigExpander.getWebSocketWsUrl());
|
||||
|
||||
// 9. 缓存用户信息,创建会话
|
||||
// 10. 缓存用户信息,创建会话
|
||||
sessionManagerApi.createSession(jwtToken, loginUser, loginRequest.getCreateCookie());
|
||||
|
||||
// 10. 如果开启了单账号单端在线,则踢掉已经上线的该用户
|
||||
// 11. 如果开启了单账号单端在线,则踢掉已经上线的该用户
|
||||
if (AuthConfigExpander.getSingleAccountLoginFlag()) {
|
||||
sessionManagerApi.removeSessionExcludeToken(jwtToken);
|
||||
}
|
||||
}
|
||||
|
||||
// 11. 更新用户登录时间和ip
|
||||
// 12. 更新用户登录时间和ip
|
||||
String ip = HttpServletUtil.getRequestClientIp(HttpServletUtil.getRequest());
|
||||
userServiceApi.updateUserLoginInfo(loginUser.getUserId(), new Date(), ip);
|
||||
|
||||
// 12.登录成功日志
|
||||
// 13.登录成功日志
|
||||
loginLogServiceApi.loginSuccess(loginUser.getUserId());
|
||||
|
||||
// 13. 组装返回结果
|
||||
// 14. 组装返回结果
|
||||
return new LoginResponse(loginUser, jwtToken, defaultJwtPayload.getExpirationDate());
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用远程接口获取loginCode
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/2/26 15:15
|
||||
*/
|
||||
private String getRemoteLoginCode(LoginRequest loginRequest) {
|
||||
|
||||
// 获取sso的地址
|
||||
String ssoUrl = AuthConfigExpander.getSsoUrl();
|
||||
|
||||
// 请求sso服务获取loginCode
|
||||
HttpRequest httpRequest = HttpRequest.post(ssoUrl + AuthConstants.SYS_AUTH_SSO_GET_LOGIN_CODE);
|
||||
httpRequest.body(JSON.toJSONString(loginRequest));
|
||||
HttpResponse httpResponse = httpRequest.execute();
|
||||
|
||||
// 获取返回结果的message
|
||||
String body = httpResponse.body();
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
if (StrUtil.isNotBlank(body)) {
|
||||
jsonObject = JSON.parseObject(body);
|
||||
}
|
||||
|
||||
// 如果返回结果是失败的
|
||||
if (httpResponse.getStatus() != 200) {
|
||||
String message = jsonObject.getString("message");
|
||||
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, message);
|
||||
}
|
||||
|
||||
// 从body中获取loginCode
|
||||
String loginCode = jsonObject.getString("data");
|
||||
|
||||
// loginCode为空
|
||||
if (loginCode == null) {
|
||||
throw new AuthException(AuthExceptionEnum.SSO_LOGIN_CODE_GET_ERROR, "loginCode为空");
|
||||
}
|
||||
|
||||
return loginCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright [2020-2030] [https://www.stylefeng.cn]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* Guns采用APACHE LICENSE 2.0开源协议,您在使用过程中,需要注意以下几点:
|
||||
*
|
||||
* 1.请不要删除和修改根目录下的LICENSE文件。
|
||||
* 2.请不要删除和修改Guns源码头部的版权声明。
|
||||
* 3.请保留源码和相关描述文件的项目出处,作者声明等。
|
||||
* 4.分发源码时候,请注明软件出处 https://gitee.com/stylefeng/guns
|
||||
* 5.在修改包名,模块名称,项目代码等时,请注明软件出处 https://gitee.com/stylefeng/guns
|
||||
* 6.若您的项目无法满足以上几点,可申请商业授权
|
||||
*/
|
||||
package cn.stylefeng.roses.kernel.auth.starter;
|
||||
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.SsoProperties;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
/**
|
||||
* 单点配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:29
|
||||
*/
|
||||
@Configuration
|
||||
public class GunsSsoAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 单点的开关配置
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:29
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sso")
|
||||
public SsoProperties ssoProperties() {
|
||||
return new SsoProperties();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
cn.stylefeng.roses.kernel.auth.starter.GunsAuthAutoConfiguration
|
||||
cn.stylefeng.roses.kernel.auth.starter.GunsAuthAutoConfiguration,\
|
||||
cn.stylefeng.roses.kernel.auth.starter.GunsSsoAutoConfiguration
|
|
@ -63,6 +63,13 @@ public class DefaultJwtPayload {
|
|||
*/
|
||||
private Long expirationDate;
|
||||
|
||||
/**
|
||||
* 单点认证中心的用户会话id,一般为一个uuid
|
||||
* <p>
|
||||
* 用来校验单点中心是否有本用户的会话
|
||||
*/
|
||||
private String caToken;
|
||||
|
||||
/**
|
||||
* 其他载体信息
|
||||
*/
|
||||
|
@ -71,11 +78,12 @@ public class DefaultJwtPayload {
|
|||
public DefaultJwtPayload() {
|
||||
}
|
||||
|
||||
public DefaultJwtPayload(Long userId, String account, boolean rememberMe) {
|
||||
public DefaultJwtPayload(Long userId, String account, boolean rememberMe, String caToken) {
|
||||
this.userId = userId;
|
||||
this.account = account;
|
||||
this.uuid = IdUtil.fastUUID();
|
||||
this.rememberMe = rememberMe;
|
||||
this.caToken = caToken;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import cn.stylefeng.roses.kernel.auth.api.AuthServiceApi;
|
|||
import cn.stylefeng.roses.kernel.auth.api.context.LoginContext;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginResponse;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.auth.LoginWithTokenRequest;
|
||||
import cn.stylefeng.roses.kernel.auth.api.pojo.login.LoginUser;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.response.ResponseData;
|
||||
import cn.stylefeng.roses.kernel.rule.pojo.response.SuccessResponseData;
|
||||
|
@ -84,6 +85,18 @@ public class LoginController {
|
|||
return new SuccessResponseData(loginResponse.getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于token登录,适用于单点登录
|
||||
*
|
||||
* @author fengshuonan
|
||||
* @date 2021/5/25 22:36
|
||||
*/
|
||||
@PostResource(name = "适用于单点登录", path = "/loginWithToken", requiredLogin = false, requiredPermission = false)
|
||||
public ResponseData loginWithToken(@RequestBody @Validated LoginWithTokenRequest loginWithTokenRequest) {
|
||||
LoginResponse loginResponse = authServiceApi.LoginWithToken(loginWithTokenRequest);
|
||||
return new SuccessResponseData(loginResponse.getToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登出
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue