Initial commit

This commit is contained in:
jackyu66git
2026-03-23 11:52:51 +08:00
commit ea8d889fbe
1114 changed files with 92438 additions and 0 deletions
+18
View File
@@ -0,0 +1,18 @@
FROM openjdk:8-jre
MAINTAINER oneone oneonetech@163.com
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \&& echo 'Asia/Shanghai' >/etc/timezone
# /tmp 目录作为容器数据卷目录,SpringBoot内嵌Tomcat容器默认使用/tmp作为工作目录,任何向 /tmp 中写入的信息不会记录进容器存储层,从而保证容器存储层的无状态化
# 在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录
VOLUME /tmp
# 复制jar到镜像
ADD target/oneone-auth.jar app.jar
ENTRYPOINT ["java", "-Xmx128m", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app.jar"]
EXPOSE 8000
+135
View File
@@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>oneone-cloud</artifactId>
<groupId>com.oneone.cloud</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>oneone-auth</artifactId>
<dependencies>
<!--Spring Cloud & Alibaba -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- 注册中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>common-web</artifactId>
</dependency>
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>common-redis</artifactId>
</dependency>
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency>
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.16.5</version>
</dependency>
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>common-core</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
</dependency>
<!-- fegin api 相关 -->
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>common-sms</artifactId>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--kafka-->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.22.113.ALL</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>alert-api</artifactId>
</dependency>
<dependency>
<groupId>com.oneone.cloud</groupId>
<artifactId>oneone-pms-api</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,21 @@
package com.oneone.auth;
import com.oneone.alert.api.feign.AlertFeignClient;
import com.oneone.pms.api.account.IPlatformAccountApi;
import com.oneone.pms.api.loginconfig.IThirdLoginConfigApi;
import com.oneone.upms.api.permission.PermissionApi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(basePackageClasses = {IPlatformAccountApi.class, AlertFeignClient.class
, IThirdLoginConfigApi.class , PermissionApi.class})
@EnableDiscoveryClient
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
@@ -0,0 +1,67 @@
package com.oneone.auth.common;
import com.oneone.common.exception.BusinessException;
import com.oneone.common.result.Result;
import com.oneone.pms.api.loginconfig.IThirdLoginConfigApi;
import com.oneone.pms.api.loginconfig.dto.AppThirdLoginDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
@Component
public class AppThirdLoginConfigInfo {
@Autowired
private IThirdLoginConfigApi thirdLoginConfigApi;
private static final String SEPARATOR="!-_-!";
private HashMap<String, AppThirdLoginDTO> LOCAL_CACHE_DOWN = new HashMap();
private Long minute=1000*60L;
public AppThirdLoginDTO getLoginConfigWithCache( String type){
AppThirdLoginDTO dto=LOCAL_CACHE_DOWN.get(type);
if(dto==null|| dto.getLastUpdateTime()+5*minute<System.currentTimeMillis()){
dto=getConfig(type);
if(dto==null){
dto=new AppThirdLoginDTO();
dto.setLastUpdateTime(System.currentTimeMillis());
LOCAL_CACHE_DOWN.put(type,dto);
throw new BusinessException("登录失败,请稍后再试");
}else{
dto.setLastUpdateTime(System.currentTimeMillis());
LOCAL_CACHE_DOWN.put(type,dto);
return dto;
}
}
if(dto.getThirdAppId()==null){
throw new BusinessException("登录失败,请稍后再试");
}
return dto;
}
public AppThirdLoginDTO getLoginConfig(String type){
AppThirdLoginDTO dto=getConfig(type);
if(dto==null){
throw new BusinessException("登录失败,请稍后再试");
}
return dto;
}
private AppThirdLoginDTO getConfig(String type){
Result<AppThirdLoginDTO> result = thirdLoginConfigApi.thirdLoginConfig(type);
if ( result == null || !Result.isSuccess(result) ) {
throw new BusinessException("登录失败,请稍后再试");
}
AppThirdLoginDTO dto=result.getData();
if(dto==null){
return null;
}
return dto;
}
public void freshCache() {
LOCAL_CACHE_DOWN.clear();
}
}
@@ -0,0 +1,39 @@
package com.oneone.auth.common;
import com.google.common.hash.Hashing;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Base64;
/**
* 加密
*/
public class CryptalUtil {
private static final String MAC_NAME = "HmacSHA1";
private static final String ENCODING = "UTF-8";
public static byte[] HmacSHA1Encrypt(String encryptText,String encryptKey) throws Exception {
byte[] data = encryptKey.getBytes(ENCODING);
//根据给定的字节数组构造一个密钥,第二参数指定一个密钥算法的名称
SecretKey secretKey = new SecretKeySpec(data,MAC_NAME);
//生成一个指定 Mac 算法 的 Mac 对象
Mac mac =Mac.getInstance(MAC_NAME);
//用给定密钥初始化 Mac 对象
mac.init(secretKey);
byte[] text = encryptText.getBytes(ENCODING);
return mac.doFinal(text);
}
public static String hash_hmac(String encryptText,String encryptKey) throws Exception{
byte[] bytes = HmacSHA1Encrypt(encryptText,encryptKey);
return Base64.getEncoder().encodeToString(bytes);
}
public static String md5(String str){
return Hashing.md5().hashString(str, Charset.forName("UTF-8")).toString().toLowerCase();
}
}
@@ -0,0 +1,19 @@
package com.oneone.auth.common.ali;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author mice
* @version 1.0
* @date 2023-03-15 11:44
*/
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AliProperty {
private String appId;
private String privateKey;
private String publicKey;
}
@@ -0,0 +1,61 @@
package com.oneone.auth.common.ali;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.oneone.auth.common.AppThirdLoginConfigInfo;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.common.exception.BusinessException;
import com.oneone.pms.api.loginconfig.dto.AppThirdLoginDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author mice
* @version 1.0
* @date 2023-03-15 11:43
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class AliUtil {
// private final AliProperty aliProperty;
@Autowired
private AppThirdLoginConfigInfo appThirdLoginConfigInfo;
/**
* 授权码登录
* @param code
* @return
*/
public AlipaySystemOauthTokenResponse login(String code){
AppThirdLoginDTO aliProperty=appThirdLoginConfigInfo.getLoginConfigWithCache( GrantTypeEnum.ALIPAY.getCode());
AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",aliProperty.getThirdAppId(),aliProperty.getPrivateKey(),"json","GBK",aliProperty.getPublicKey(),"RSA2");
AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
request.setGrantType("authorization_code");
request.setCode(code);
AlipaySystemOauthTokenResponse response = null;
try {
response = alipayClient.execute(request);
} catch (AlipayApiException e) {
log.error("调用阿里授权API失败:{},{}",e.getErrCode(),e.getErrMsg());
throw new BusinessException("登录失败,请稍后再试");
}
if(response.isSuccess()){
return response;
} else {
log.error("调用阿里授权API失败:{},{}",response.getCode());
throw new BusinessException("登录失败,请稍后再试");
}
}
}
@@ -0,0 +1,104 @@
package com.oneone.auth.common.apple;
import cn.hutool.http.HttpUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.auth0.jwk.Jwk;
import io.jsonwebtoken.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.security.PublicKey;
/**
* @author mice
* @version 1.0
* @date 2022-06-13 11:44
*/
@Data
@Slf4j
public class AppleUtil {
private static final String AUTH_KEYS_URL = "https://appleid.apple.com/auth/keys";
private static final String APPLE_ID_URL = "https://appleid.apple.com";
public static String validateToken(String token){
JWT jwt = JWTUtil.parseToken(token);
String kid = jwt.getHeader().getClaimsJson().get("kid").toString();
cn.hutool.json.JSONObject jsonObject = jwt.getPayload().getClaimsJson();
String aud = jsonObject.get("aud").toString();
String sub = jsonObject.get("sub").toString();
boolean isSuccess = verifyToken(token , kid, aud, sub);
if (isSuccess){
return sub;
}else {
return null;
}
}
public static boolean verifyToken(String identityToken, String kid, String aud, String sub) {
PublicKey publicKey = getPublicKey(kid);
JwtParser jwtParser = Jwts.parser().setSigningKey(publicKey);
jwtParser.requireIssuer(APPLE_ID_URL);
jwtParser.requireAudience(aud);
jwtParser.requireSubject(sub);
try {
Jws<Claims> claim = jwtParser.parseClaimsJws(identityToken);
if (claim != null && claim.getBody().containsKey("auth_time")) {
return true;
}
return false;
} catch (ExpiredJwtException e) {
log.error("苹果登陆授权 idToken 已过期", e);
return false;
} catch (Exception e) {
log.error("非法的苹果登陆授权 idToken", e);
return false;
}
}
/**
* 通过kid查找苹果中获取对应的PublicKey
* @param kid
* @return 构造好的公钥
*/
private static PublicKey getPublicKey(String kid) {
try {
JSONArray jsonArray = getAuthKeys();
if (jsonArray.isEmpty()) {
return null;
}
for (Object object : jsonArray) {
JSONObject json= (JSONObject) JSONObject.toJSON(object);
if (json.getString("kid").equals(kid)) {
json= (JSONObject) JSONObject.toJSON(object);
Jwk jwa = Jwk.fromValues(json);
return jwa.getPublicKey();
}
}
} catch (final Exception e) {
log.error("apple getPublicKey error", e);
}
return null;
}
/**
* 获取苹果的公钥
*
* @return
*/
private static JSONArray getAuthKeys() {
String result = HttpUtil.get(AUTH_KEYS_URL);
JSONObject json = JSONObject.parseObject(result);
return json != null ? json.getJSONArray("keys") : null;
}
}
@@ -0,0 +1,25 @@
package com.oneone.auth.common.enums;
import com.oneone.common.exception.ErrorCode;
public interface ErrorCodeConstants {
ErrorCode USER_LOGIN_ERROR = new ErrorCode(1_004_001_001, "用户登录异常");
ErrorCode USER_NOT_EXIST = new ErrorCode(1_004_001_002, "用户不存在");
ErrorCode USER_ACCOUNT_LOCKED = new ErrorCode(1_004_001_003, "用户账户被冻结");
ErrorCode USER_ACCOUNT_INVALID = new ErrorCode(1_004_001_004, "用户账户已作废");
ErrorCode USER_EXIST = new ErrorCode(1_004_001_005, "账号已存在");
ErrorCode USERNAME_OR_PASSWORD_ERROR = new ErrorCode(1_004_001_006, "用户名或密码错误");
ErrorCode PASSWORD_ENTER_EXCEED_LIMIT = new ErrorCode(1_004_001_007, "用户输入密码次数超限");
ErrorCode CLIENT_AUTHENTICATION_FAILED = new ErrorCode(1_004_001_008, "客户端认证失败");
ErrorCode TOKEN_INVALID_OR_EXPIRED = new ErrorCode(1_004_001_009, "token无效或已过期");
ErrorCode TOKEN_ACCESS_FORBIDDEN = new ErrorCode(1_004_001_010, "token已被禁止访问");
ErrorCode AUTHORIZED_ERROR = new ErrorCode(1_004_001_011, "访问权限异常");
ErrorCode USER_HAS_BAND_ERROR = new ErrorCode(1_004_001_002, "用户已绑定该登录方式");
ErrorCode GRANT_TYPE_HAS_BAND_ERROR = new ErrorCode(1_004_001_003, "登录方式已被其他账号绑定");
}
@@ -0,0 +1,47 @@
package com.oneone.auth.common.enums;
import lombok.Getter;
/**
* @author oneone
* @description
* @createTime 2021/6/5 17:57
*/
public enum GrantTypeEnum {
REFRESH("refresh","刷新token"),
SMS_CODE("sms_code","短信验证码登录"),
WECHAT("wechat","微信登录"),
WECHAT_CODE("wechatCode","微信登录授权码"),
MINI_APP("mini_app","微信小程序"),
OFFICIAL_ACCOUNT("official_account","公众号"),
APPLE_JWT("apple","苹果jwt"),
USERNAME("username","账号"),
EMAIL("email","邮件"),
TWITTER("twitter","twitter"),
ALIPAY("alipay","支付吧"),
DEVICE("device","设备号,游客登录"),
FACEBOOK("facebook","脸书登录"),
GOOGLE("google","谷歌登录"),
;
public static GrantTypeEnum getByCode(String code){
for (GrantTypeEnum value : values()) {
if (value.getCode().equals(code)){
return value;
}
}
return null;
}
@Getter
private String code;
@Getter
private String desc;
GrantTypeEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
}
@@ -0,0 +1,23 @@
package com.oneone.auth.common.enums;
import lombok.Getter;
/**
* @author oneone
* @description TODO
* @createTime 2021/6/5 17:57
*/
public enum PasswordEncoderTypeEnum {
BCRYPT("{bcrypt}","BCRYPT加密"),
NOOP("{noop}","无加密明文");
@Getter
private String prefix;
PasswordEncoderTypeEnum(String prefix, String desc){
this.prefix=prefix;
}
}
@@ -0,0 +1,26 @@
package com.oneone.auth.common.enums;
import lombok.Getter;
/**
* @author oneone
* @description
* @createTime 2021/6/5 17:57
*/
public enum WechatLoginTypeEnum {
CLIENT_CREDENTIAL("client_credential","客户端凭证"),
AUTHORIZATION_CODE("authorization_code","授权码");
@Getter
private String code;
@Getter
private String desc;
WechatLoginTypeEnum(String code,String desc){
this.code = code;
this.desc = desc;
}
}
@@ -0,0 +1,99 @@
package com.oneone.auth.common.twitter;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.oneone.auth.common.AppThirdLoginConfigInfo;
import com.oneone.auth.common.CryptalUtil;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.common.web.util.HttpClientUtil;
import com.oneone.pms.api.loginconfig.dto.AppThirdLoginDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
/**
* @author mice
* @version 1.0
* @date 2022-10-06 20:47
*/
@Slf4j
@Component
@ConditionalOnProperty(name = "login.twitter.enabled")
public class TwitterUtil {
// @Value("${login.twitter.consumer-key}")
// private String consumerKey;
// @Value("${login.twitter.consumer-secret}")
// private String consumerSecret;
@Autowired
private HttpClientUtil httpClientUtil;
@Autowired
private AppThirdLoginConfigInfo appThirdLoginConfigInfo;
private final String TW_URL = "https://api.twitter.com/1.1/account/verify_credentials.json";
/**
* 检测twitter的登录
* @param accessToken
* @return
*/
public String checkTwitterToken(String accessToken,String accessTokenSecret){
AppThirdLoginDTO configDTO=appThirdLoginConfigInfo.getLoginConfigWithCache(GrantTypeEnum.TWITTER.getCode());
String consumerKey=configDTO.getThirdAppId();
String consumerSecret=configDTO.getPrivateKey();
String oauth_nonce = RandomUtil.randomString(42);
long oauth_timestamp = System.currentTimeMillis() / 1000;
String signingKey = consumerSecret + "&" + accessTokenSecret;
StringBuilder signatrueBaseStr = new StringBuilder();
String oauth_signature = null;
StringBuilder paramStr = new StringBuilder();
paramStr.append("oauth_consumer_key=").append(consumerKey)
.append("&oauth_nonce=").append(oauth_nonce)
.append("&oauth_signature_method=HMAC-SHA1")
.append("&oauth_timestamp=").append(oauth_timestamp)
.append("&oauth_token=").append(accessToken)
.append("&oauth_version=1.0");
try {
signatrueBaseStr.append("GET&").append(URLEncoder.encode(TW_URL,"UTF-8"))
.append("&").append(URLEncoder.encode(paramStr.toString(),"UTF-8"));
oauth_signature = CryptalUtil.hash_hmac(signatrueBaseStr.toString(), signingKey);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
try {
Map<String,Object> headMap = Maps.newHashMap();
StringBuilder headParam = new StringBuilder();
headParam.append("OAuth oauth_consumer_key=\"").append(consumerKey).append("\", ")
.append("oauth_nonce=\"").append(oauth_nonce).append("\", ")
.append("oauth_signature=\"").append(URLEncoder.encode(oauth_signature,"UTF-8")).append("\", ")
.append("oauth_signature_method=\"HMAC-SHA1\", ")
.append("oauth_timestamp=\"").append(oauth_timestamp).append("\", ")
.append("oauth_token=\"").append(accessToken).append("\", ")
.append("oauth_version=\"1.0\"");
headMap.put("Authorization",headParam.toString());
Map<String,Object> paramMap = Maps.newHashMap();
String result = httpClientUtil.doGet(TW_URL, headMap,paramMap);
if(!Strings.isNullOrEmpty(result)){
JSONObject jsonObject = JSON.parseObject(result);
String id = jsonObject.getString("id");
return id;
}
} catch (Exception e) {
log.error("checkTwitterToken is error : ",e);
}
return null;
}
}
@@ -0,0 +1,20 @@
package com.oneone.auth.common.wechat;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
public class MiniAppParam implements Serializable {
@NotNull
private String encryptedData;
@NotNull
private String iv;
@NotNull
private String code;
}
@@ -0,0 +1,20 @@
package com.oneone.auth.common.wechat;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@Data
public class OfficialAccountParam implements Serializable {
private String phone;
private String validateCode;
@NotNull
private String code;
private String openId;
}
@@ -0,0 +1,23 @@
package com.oneone.auth.common.wechat;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author mice
* @version 1.0
* @date 2023-03-15 11:44
*/
@Data
@Component
@ConfigurationProperties(prefix = "wechat.app")
public class WechatProperty {
private String appId;
private String appSecret;
private String miniAppId;
private String miniAppSecret;
private String officialAccountAppId;
private String officialAccountAppSecret;
}
@@ -0,0 +1,33 @@
package com.oneone.auth.common.wechat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author mice
* @version 1.0
* @date 2023-03-15 13:59
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WechatToken {
//{
//
// "access_token":"ACCESS_TOKEN",
//
// "expires_in":7200,
//
// "refresh_token":"REFRESH_TOKEN",
//
// "openid":"OPENID",
//
// "scope":"SCOPE"
//
//}
private String accessToken;
private String openId;
private String sessionKey;
private String unionId;
}
@@ -0,0 +1,168 @@
package com.oneone.auth.common.wechat;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import cn.binarywang.wx.miniapp.util.crypt.WxMaCryptUtils;
import cn.hutool.core.codec.Base64;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.oneone.auth.common.AppThirdLoginConfigInfo;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.auth.config.WechatUserInfo;
import com.oneone.pms.api.loginconfig.dto.AppThirdLoginDTO;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.HashMap;
import java.util.Map;
@Component
public class WechatUtil {
// @Autowired
// private WechatProperty wechatProperty;
@Autowired
private AppThirdLoginConfigInfo appThirdLoginConfigInfo;
private static Logger logger = LoggerFactory.getLogger(WechatUtil.class);
public WechatUserInfo getUserInfo(String code) {
try {
WechatToken wechatToken = getOauth2AccessToken( code);
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID";
logger.info("微信用户信息接口返回:{}",requestUrl);
requestUrl = requestUrl.replace("ACCESS_TOKEN", wechatToken.getAccessToken()).replace("OPENID", wechatToken.getOpenId());
// 通过网页授权获取用户信息
String s = HttpUtil.get(requestUrl);
WechatUserInfo wechatUserInfo = JSONObject.parseObject(s,WechatUserInfo.class);
return wechatUserInfo;
}catch (Exception e){
logger.error("获取用户信息失败",e);
return null;
}
}
/**
* 获取网页授权凭证
*
* @param code
* @return WeixinAouth2Token
*/
public WechatToken getOauth2AccessToken(String code) {
AppThirdLoginDTO wechatProperty=appThirdLoginConfigInfo.getLoginConfigWithCache(GrantTypeEnum.WECHAT_CODE.getCode());
WechatUserInfo wat = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", wechatProperty.getThirdAppId());
requestUrl = requestUrl.replace("SECRET", wechatProperty.getPrivateKey());
requestUrl = requestUrl.replace("CODE", code);
// 获取网页授权凭证
String result = HttpUtil.get(requestUrl);
logger.info("微信授权码接口返回:{}",result);
JSONObject jsonObject = JSONObject.parseObject(result);
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
WechatToken wechatToken = new WechatToken(access_token,openid,null,null);
return wechatToken;
}
public WechatToken getOauth2AccessTokenByJsCode(String code) {
AppThirdLoginDTO wechatProperty=appThirdLoginConfigInfo.getLoginConfigWithCache(GrantTypeEnum.MINI_APP.getCode());
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", wechatProperty.getThirdAppId());
requestUrl = requestUrl.replace("SECRET", wechatProperty.getPrivateKey());
requestUrl = requestUrl.replace("CODE", code);
String result = HttpUtil.get(requestUrl);
logger.info("微信授权码接口返回:{}",result);
JSONObject jsonObject = JSONObject.parseObject(result);
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
String sessionKey = jsonObject.getString("session_key");
String unionid = jsonObject.getString("unionid");
WechatToken wechatToken = new WechatToken(access_token,openid,sessionKey,unionid);
return wechatToken;
}
public WxMaPhoneNumberInfo getPhoneNumber(String sessionKey, MiniAppParam miniAppParam) {
String result =null;
try {
byte[] encData = Base64.decode(miniAppParam.getEncryptedData());
byte[] iv = Base64.decode(miniAppParam.getIv());
byte[] key = Base64.decode(sessionKey);
AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
result = new String(cipher.doFinal(encData), "UTF-8");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
if(result==null){
return null;
}
return JSONObject.parseObject(result,WxMaPhoneNumberInfo.class);
}
/**
* 获取网页授权凭证
*
* @param code
* @return WeixinAouth2Token
*/
public WechatToken getOauth2AccessTokenByOfficialAccount(String code) {
AppThirdLoginDTO wechatProperty=appThirdLoginConfigInfo.getLoginConfigWithCache(GrantTypeEnum.MINI_APP.getCode());
WechatUserInfo wat = null;
// 拼接请求地址
String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
requestUrl = requestUrl.replace("APPID", wechatProperty.getThirdAppId());
requestUrl = requestUrl.replace("SECRET", wechatProperty.getPrivateKey());
requestUrl = requestUrl.replace("CODE", code);
// 获取网页授权凭证
String result = HttpUtil.get(requestUrl);
logger.info("微信授权码接口返回:{}",result);
JSONObject jsonObject = JSONObject.parseObject(result);
String access_token = jsonObject.getString("access_token");
String openid = jsonObject.getString("openid");
WechatToken wechatToken = new WechatToken(access_token,openid,null,null);
return wechatToken;
}
}
@@ -0,0 +1,40 @@
package com.oneone.auth.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Slf4j
@Component
@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/*")
public class ApiAccessFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) {
log.info("过滤器名称:ApiAccessFilter");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
long start = System.currentTimeMillis(); // 请求进入时间
filterChain.doFilter(servletRequest, servletResponse);
log.info("请求地址: {}, 耗时: {}", request.getRequestURI(), System.currentTimeMillis()-start);
}
@Override
public void destroy() {
}
}
@@ -0,0 +1,36 @@
package com.oneone.auth.config;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import com.oneone.common.redis.utils.RedisUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author mice
* @version 1.0
* @date 2022-04-07 16:44
*/
@Configuration
public class RedisStoreConfig {
@Bean(value = "appRedisTokenStore")
public LocalRedisTokenStore appRedisTokenStore(RedisUtils redisUtils){
LocalRedisTokenStore appRedisTokenStore = new LocalRedisTokenStore(redisUtils);
appRedisTokenStore.setPrefix("app_token:");
return appRedisTokenStore;
}
// @Bean(value = "miniAppRedisTokenStore")
// public LocalRedisTokenStore sysRedisTokenStore(RedisUtils redisUtils){
// LocalRedisTokenStore sysRedisTokenStore = new LocalRedisTokenStore(redisUtils);
// sysRedisTokenStore.setPrefix("mini_app_token:");
// return sysRedisTokenStore;
// }
//
// @Bean(value = "officialAccountRedisTokenStore")
// public LocalRedisTokenStore officialAccountRedisTokenStore(RedisUtils redisUtils){
// LocalRedisTokenStore appRedisTokenStore = new LocalRedisTokenStore(redisUtils);
// appRedisTokenStore.setPrefix("official_account_token:");
// return appRedisTokenStore;
// }
}
@@ -0,0 +1,28 @@
package com.oneone.auth.config;
import lombok.Data;
/**
* 微信用户信息
*
* @author oneone
* @date 2021/10/4
*/
@Data
public class WechatUserInfo {
private String headimgurl;
private String city;
private String country;
private Integer gender;
private String language;
private String nickname;
private String province;
private String openid;
}
@@ -0,0 +1,30 @@
package com.oneone.auth.controller.admin;
import com.oneone.auth.common.wechat.WechatProperty;
import com.oneone.auth.entity.dto.OfficialAccountDTO;
import com.oneone.common.result.Result;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Tag(name = "app - 公众号")
@RestController
@RequestMapping("oauth/officialAccount")
@AllArgsConstructor
@Slf4j
public class OfficialAccountController {
private final WechatProperty wechatProperty;
@Operation(summary = "获取appid", description = "获取appid")
@GetMapping("/getAppid")
public Result<OfficialAccountDTO> getAppid() {
OfficialAccountDTO officialAccountDTO = new OfficialAccountDTO();
officialAccountDTO.setAppId(wechatProperty.getOfficialAccountAppId());
return Result.success(officialAccountDTO);
}
}
@@ -0,0 +1,60 @@
package com.oneone.auth.controller.app;
import com.oneone.auth.entity.TokenParam;
import com.oneone.auth.entity.ValidateTokenParam;
import com.oneone.auth.controller.app.vo.LocalTokenVO;
import com.oneone.auth.service.SsoService;
import com.oneone.common.base.LocalToken;
import com.oneone.common.result.Result;
import com.oneone.common.web.util.UserContext;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Tag(name = "app - 认证中心")
@RestController
@RequestMapping("/oauth/app")
@AllArgsConstructor
@Slf4j
public class AppSsoController {
private final SsoService appSsoService;
@Operation(summary = "登录")
@PostMapping("/login")
public Result<LocalToken> login(@RequestBody TokenParam param) {
param.setPlatformUserId(null);
return Result.success(appSsoService.login(param));
}
@Operation(summary = "注册")
@PostMapping("/register")
public Result<LocalTokenVO> register(@RequestBody TokenParam param) {
return Result.success(LocalTokenVO.getLocalTokenDTO(appSsoService.register(param)));
}
@Operation(summary = "登出")
@PostMapping("/logout")
public Result logout(@RequestHeader("Authorization") String authorization) {
appSsoService.logout(authorization);
return Result.success("退出成功");
}
@Operation(summary = "验证token")
@PostMapping("/validateToken")
public Result<LocalToken> validateToken(@RequestBody ValidateTokenParam param) {
return Result.success(appSsoService.validate(param.getAccessToken()));
}
@Operation(summary = "账号绑定")
@PostMapping("/bind")
public Result<Boolean> bind(@RequestBody TokenParam param) {
param.setPlatformUserId(UserContext.getMemberId());
return Result.success(appSsoService.bind(param));
}
}
@@ -0,0 +1,56 @@
package com.oneone.auth.controller.app;
import com.oneone.auth.entity.LoginSmsCodeParam;
import com.oneone.auth.entity.SendSmsCodeParam;
import com.oneone.common.result.Result;
import com.oneone.common.sms.service.SmsService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "短信验证码")
@RestController
@RequestMapping("/oauth/sms-code")
@RequiredArgsConstructor
public class SmsCodeController {
@Autowired
@Lazy
private SmsService smsService;
@Operation(summary = "发送登录验证码")
@PostMapping("/login")
public Result loginSmsCode(@Validated @RequestBody LoginSmsCodeParam param) {
//smsService.imageValidate(param.getKey(), param.getValidateCode());
smsService.sendSmsCode(param.getPhoneNumber(), param.getType());
return Result.success();
}
@Operation(summary = "发送短信验证码")
@PostMapping("/common")
public Result sendSmsCode(@Validated @RequestBody SendSmsCodeParam param) {
smsService.sendSmsCode(param.getPhoneNumber(), param.getType());
return Result.success();
}
// @Operation(summary = "发送短信验证码-已登录场景")
// @PostMapping("/auth")
// public Result sendAuthSmsCode(@Validated @RequestBody SendAuthSmsCodeParam param) {
// Result<MemberInfoDTO> memberInfoDTOResult = memberFeignClient.getMemberInfo(UserContext.getUserId());
// String phone = null;
// if (Result.isSuccess(memberInfoDTOResult)) {
// phone = memberInfoDTOResult.getData().getMobile();
// } else {
// throw new BusinessException("发送短信失败");
// }
// smsService.sendSmsCode(phone, param.getType());
// return Result.success();
// }
}
@@ -0,0 +1,39 @@
package com.oneone.auth.controller.app;
import com.oneone.auth.entity.TokenParam;
import com.oneone.auth.service.SsoService;
import com.oneone.common.base.LocalToken;
import com.oneone.common.result.Result;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
@Tag(name = "sys - 认证中心")
@RestController
@RequestMapping("/oauth/sys")
@AllArgsConstructor
@Slf4j
public class SysSsoController {
private final SsoService sysSsoService;
@Operation(summary = "登录", description = "登录")
@PostMapping("/login")
public Result<LocalToken> login(@RequestBody TokenParam param) {
return Result.success(sysSsoService.login(param));
}
@Operation(summary = "登出")
@PostMapping("/logout")
public Result logout(HttpServletRequest request ) {
String authorization = request.getHeader("Authorization");
sysSsoService.logout(authorization);
return Result.success("注销成功");
}
}
@@ -0,0 +1,49 @@
package com.oneone.auth.controller.app;
import com.oneone.auth.controller.app.vo.GameLoginResp;
import com.oneone.auth.controller.app.vo.LocalTokenVO;
import com.oneone.auth.entity.TokenParam;
import com.oneone.auth.entity.ValidateTokenParam;
import com.oneone.auth.service.SsoService;
import com.oneone.common.base.LocalToken;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import com.oneone.common.result.Result;
import com.oneone.common.web.util.UserContext;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* @author mice
* @version 1.0
* @date 2022-06-15 21:15
*/
@Tag(name = "sys - token管理")
@RestController
@RequestMapping("/oauth/token")
@AllArgsConstructor
@Slf4j
public class TokenAdminController {
private final LocalRedisTokenStore appRedisTokenStore;
private final SsoService appSsoService;
@Operation(summary = "移除appToken", description = "移除appToken")
@PostMapping("/removeAppToken/{memberId}")
public Result removeAppToken(@PathVariable Long memberId) {
appRedisTokenStore.removeToken(memberId);
return Result.success();
}
@Operation(summary = "移除sysToken", description = "移除sysToken")
@PostMapping("/removeSysToken/{userId}")
public Result removeSysToken(@PathVariable Long userId) {
appRedisTokenStore.removeToken(userId);
return Result.success();
}
}
@@ -0,0 +1,13 @@
package com.oneone.auth.controller.app.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class GameLoginResp implements Serializable {
private Long id;
private Integer status;
}
@@ -0,0 +1,41 @@
package com.oneone.auth.controller.app.vo;
import com.oneone.common.base.LocalToken;
import com.oneone.common.enums.ChannelEnum;
import com.oneone.common.util.BeanUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class LocalTokenVO implements Serializable {
private static final long serialVersionUID = 1L;
private String accessToken;
private String refreshToken;
@Schema(description = "有效期")
private Long expiresIn;
@Schema(description = "授权方式: refresh, sms_code, wechat")
private String grantType;
@Schema(description = "用户id")
private Long platformUserId;
@Schema(description = "账号")
private String account;
@Schema(description = "账号类型")
private String accountType;
@Schema(description = "用户类型")
private Integer userType;
@Schema(description = "渠道")
private ChannelEnum channel = ChannelEnum.LOCAL;
public static LocalTokenVO getLocalTokenDTO(LocalToken localToken){
if(localToken == null){
return null;
}
LocalTokenVO copy = BeanUtil.copy(localToken, LocalTokenVO.class);
copy.setPlatformUserId(localToken.getMemberId());
return copy;
}
}
@@ -0,0 +1,15 @@
package com.oneone.auth.entity;
import io.swagger.annotations.ApiModel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
@ApiModel
@Data
public class ImageValidateCodeParam {
@Schema(description="key: 手机端传设备号")
@NotNull
private String key;
}
@@ -0,0 +1,18 @@
package com.oneone.auth.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author mice
* @version 1.0
* @date 2022-12-12 10:35
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ImageValidateCodeVO {
private String key;
private String imageBase64;
}
@@ -0,0 +1,22 @@
package com.oneone.auth.entity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 15:47
*/
@Data
public class LoginSmsCodeParam {
@NotBlank
private String phoneNumber;
@NotBlank
private String type;
@NotBlank
private String validateCode;
@NotBlank
private String key;
}
@@ -0,0 +1,21 @@
package com.oneone.auth.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 15:47
*/
@Data
public class SendAuthSmsCodeParam {
@NotBlank
@Schema(description = "LOGIN(\"login\",\"登录\"),\n" +
" SET_OPERATE_PWD(\"setOperatePwd\",\"设置操作密码\"),\n" +
" CONFIRM_MOBILE(\"confirmMobile\",\"确认手机号\"),\n" +
" SET_MOBILE(\"setMobile\",\"设置手机号\")")
private String type;
}
@@ -0,0 +1,18 @@
package com.oneone.auth.entity;
import lombok.Data;
import javax.validation.constraints.NotBlank;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 15:47
*/
@Data
public class SendSmsCodeParam {
@NotBlank
private String phoneNumber;
@NotBlank
private String type;
}
@@ -0,0 +1,44 @@
package com.oneone.auth.entity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 11:33
*/
@Data
@EqualsAndHashCode
public class TokenParam {
@Schema(description = "账号")
private String account;
@Schema(description = "密码")
private String password;
@Schema(description = "授权方式: REFRESH(\"refresh\",\"刷新token\"),\n" +
" SMS_CODE(\"sms_code\",\"短信验证码登录\"),\n" +
" WECHAT(\"wechat\",\"微信登录\"),\n" +
" WECHAT_CODE(\"wechatCode\",\"微信登录授权码\"),\n" +
" PASSWORD(\"password\",\"密码登录\"),\n" +
" APPLE_JWT(\"apple\",\"苹果jwt\"),\n" +
" USERNAME(\"username\",\"账号\"),\n" +
" MINI_APP(\"mini_app\",\"微信小程序\")," +
" ALIPAY(\"alipay\",\"支付宝登录\")," +
" DEVICE(\"device\",\"设备号登录\")," +
" FACEBOOK(\"facebook\",\"facebook登录\")," +
" GOOGLE(\"google\",\"google登录\")," +
" EMAIL(\"email\",\"邮件\"),")
private String grantType;
@Schema(description = "refreshToken")
private String refreshToken;
private String others;
@Schema(description = "",hidden = true)
private Long platformUserId;
@Schema(description = "渠道")
private String channel;
}
@@ -0,0 +1,18 @@
package com.oneone.auth.entity;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* @author mice
* @version 1.0
* @date 2023-02-24 19:49
*/
@Data
public class ValidateTokenParam {
@NotEmpty(message = "accessToken不能为空")
private String accessToken;
@NotEmpty(message = "playerId不能为空")
private Long playerId;
}
@@ -0,0 +1,42 @@
package com.oneone.auth.entity.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 10:36
*/
@Data
public class AccountInfo {
/**
* 用户id
*/
@Schema(description="用户id")
private Long platformUserId;
/**
* 账号类型
*/
@Schema(description="账号类型")
private String accountType;
/**
* 账号
*/
@Schema(description="账号")
private String account;
/**
* 密码
*/
@Schema(description="密码")
private String password;
/**
* 1:启用 0:禁用
*/
@Schema(description="1:启用 0:禁用")
private Integer status;
}
@@ -0,0 +1,6 @@
package com.oneone.auth.entity.dto;
@lombok.Data
public class OfficialAccountDTO implements java.io.Serializable{
private String appId;
}
@@ -0,0 +1,22 @@
package com.oneone.auth.service;
import com.oneone.auth.entity.dto.AccountInfo;
import com.oneone.pms.api.account.vo.RegisterParam;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 11:42
*/
public interface AccountInfoService {
AccountInfo getAccountInfo(String accountType, String account);
AccountInfo register(String accountType,String account);
Boolean bind(Long platformUserId,String accountType,String account,String password);
Boolean bind(Long platformUserId,String accountType,String account);
AccountInfo register(String accountType,String account,String password);
AccountInfo register(RegisterParam param);
}
@@ -0,0 +1,29 @@
package com.oneone.auth.service;
import com.oneone.common.base.LocalToken;
public interface BindService {
Boolean smsCode(String mobile, String smsCode,Long platformUserId);
Boolean username(String username, String password,Long platformUserId);
Boolean email(String email, String code,Long platformUserId);
Boolean twitter(String account,String password,Long platformUserId);
Boolean alipay(String code,Long platformUserId);
Boolean wechat(String code,Long platformUserId);
Boolean miniApp(String code,String miniAppParamStr,Long platformUserId);
Boolean officialAccount(String officialAccountParamStr,Long platformUserId);
Boolean appleJwt(String token,Long platformUserId);
Boolean device(String deviceId,Long platformUserId);
Boolean facebook(String token,Long platformUserId);
Boolean google(String token,Long platformUserId);
}
@@ -0,0 +1,38 @@
package com.oneone.auth.service;
import com.oneone.common.base.LocalToken;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 11:40
*/
public interface LoginService {
LocalToken freshToken(String freshToken);
void logout(String accessToken);
LocalToken smsCode(String mobile, String smsCode);
LocalToken username(String username, String password);
LocalToken email(String email, String code);
LocalToken twitter(String account,String password);
LocalToken alipay(String code);
LocalToken wechat(String code);
LocalToken miniApp(String code,String miniAppParamStr);
LocalToken officialAccount(String officialAccountParamStr);
LocalToken appleJwt(String token);
LocalToken device(String deviceId);
LocalToken facebook(String facebookUid);
LocalToken google(String uid);
}
@@ -0,0 +1,18 @@
package com.oneone.auth.service;
import com.oneone.common.base.LocalToken;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 12:05
*/
public interface RegisterService {
LocalToken mobile(String account,String password);
LocalToken username(String account,String password);
LocalToken email(String account,String password);
LocalToken twitter(String accessToken,String accessTokenSecret);
}
@@ -0,0 +1,23 @@
package com.oneone.auth.service;
import com.oneone.common.base.LocalToken;
import com.oneone.auth.entity.TokenParam;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 11:37
*/
public interface SsoService {
LocalToken register(TokenParam param);
LocalToken login(TokenParam param);
Boolean bind(TokenParam param);
void logout(String authorization);
LocalToken validate(String accessToken);
}
@@ -0,0 +1,111 @@
package com.oneone.auth.service.impl;
import cn.hutool.core.util.CharsetUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.oneone.auth.common.enums.ErrorCodeConstants;
import com.oneone.auth.entity.dto.AccountInfo;
import com.oneone.auth.service.AccountInfoService;
import com.oneone.common.enums.AccountTypeEnum;
import com.oneone.common.exception.BusinessException;
import com.oneone.common.redis.lock.Lock;
import com.oneone.common.result.Result;
import com.oneone.common.util.JsonUtils;
import com.oneone.pms.api.account.IPlatformAccountApi;
import com.oneone.pms.api.account.dto.UserLoginAccountInfoDTO;
import com.oneone.pms.api.account.vo.RegisterParam;
import com.oneone.pms.api.account.vo.UserLoginAccountParam;
import io.swagger.v3.core.util.Json;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 11:43
*/
@RequiredArgsConstructor
@Service
@Slf4j
public class AccountInfoServiceImpl implements AccountInfoService {
private final IPlatformAccountApi platformAccountApi;
@Override
public AccountInfo getAccountInfo(String accountType, String account) {
Result<UserLoginAccountInfoDTO> result = platformAccountApi.getUserLoginAccountInfo(new UserLoginAccountParam(accountType, account));
if (Result.isSuccess(result) && result.getData() != null) {
UserLoginAccountInfoDTO memberAccountInfo = result.getData();
AccountInfo accountInfo = new AccountInfo();
BeanUtils.copyProperties(memberAccountInfo,accountInfo);
return accountInfo;
}else {
return null;
}
}
@Override
@Lock(key = "account_register",spel = "#account")
public Boolean bind(Long platformUserId, String accountType, String account, String password) {
RegisterParam param=new RegisterParam(platformUserId,accountType,account,password);
Result<Boolean> result = platformAccountApi.bindAccount(param);
if (Result.isSuccess(result)&&result.getData()) {
return true;
}
return false;
}
@Override
@Lock(key = "account_register",spel = "#account")
public Boolean bind(Long platformUserId, String accountType, String account) {
return this.bind(platformUserId,accountType,account,null);
}
@Override
@Lock(key = "account_register",spel = "#account")
public AccountInfo register(String accountType,String account) {
return this.register(new RegisterParam(accountType,account));
}
@Lock(key = "account_register",spel = "#account")
@Override
public AccountInfo register(String accountType, String account, String password) {
String encodePassword = null;
if(AccountTypeEnum.USERNAME.getCode().equals(accountType)){
if (!StringUtils.isEmpty(password)) {
encodePassword = MD5Utils.md5Hex(password, CharsetUtil.UTF_8);
}
}else{
encodePassword = password;
}
RegisterParam param=new RegisterParam(accountType,account);
param.setPassword(encodePassword);
return this.register(param);
}
@Lock(key = "account_register",spel = "#param.account")
@Override
public AccountInfo register(RegisterParam param) {
if(AccountTypeEnum.MOBILE.getCode().equals(param.getAccountType())){
if (StringUtils.isEmpty(param.getPhone())) {
param.setPhone(param.getAccount());
}
}
Result<UserLoginAccountInfoDTO> result = platformAccountApi.register(param);
if (Result.isSuccess(result) && result.getData() != null) {
UserLoginAccountInfoDTO memberAccountInfo = result.getData();
AccountInfo accountInfo = new AccountInfo();
BeanUtils.copyProperties(memberAccountInfo, accountInfo);
return accountInfo;
} else {
log.error("register error, RegisterParam:{}, result:{}", JsonUtils.toJSONString(param), result);
throw new BusinessException(ErrorCodeConstants.USER_EXIST);
}
}
}
@@ -0,0 +1,186 @@
package com.oneone.auth.service.impl;
import com.oneone.alert.api.client.ActionClient;
import com.oneone.alert.api.common.AlterConstant;
import com.oneone.alert.api.param.ActionDataParam;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.auth.entity.TokenParam;
import com.oneone.auth.service.BindService;
import com.oneone.auth.service.LoginService;
import com.oneone.auth.service.RegisterService;
import com.oneone.auth.service.SsoService;
import com.oneone.common.base.LocalToken;
import com.oneone.common.exception.BusinessException;
import com.oneone.common.redis.lock.Lock;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import com.oneone.common.result.Result;
import com.oneone.common.util.DateUtils;
import com.oneone.common.util.Func;
import com.oneone.pms.api.account.IPlatformAccountApi;
import com.oneone.pms.api.account.dto.PlatformUserInfoDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.util.Objects;
import static com.oneone.auth.common.enums.ErrorCodeConstants.AUTHORIZED_ERROR;
import static com.oneone.auth.common.enums.ErrorCodeConstants.USER_HAS_BAND_ERROR;
import static com.oneone.common.result.ResultCode.TOKEN_INVALID_OR_EXPIRED;
/**
* @author mice
* @version 1.0
* @date 2022-04-02 11:37
*/
@Slf4j
@Service("appSsoService")
@RequiredArgsConstructor
public class AppSsoServiceImpl implements SsoService {
private final LoginService loginService;
private final BindService bindService;
private final RegisterService registerService;
private final LocalRedisTokenStore appRedisTokenStore;
private final IPlatformAccountApi platformAccountApi;
private final ActionClient actionClient;
@Override
@Lock(key = "app_login", spel = "#param.hashCode()")
public LocalToken login(TokenParam param) {
LocalToken localToken = generateToken(param);
actionClient.push(ActionDataParam.builder()
.memberId(localToken.getMemberId())
.businessType(AlterConstant.BusinessType.LOGIN.getCode())
.dataField1(param.getGrantType())
.dataField2(param.getChannel())
.build());
return localToken;
}
@Override
public Boolean bind(TokenParam param) {
if(GrantTypeEnum.REFRESH.getCode().equals(param.getGrantType())){
log.error("grantType is error:{}", param.getGrantType());
throw new BusinessException("grantType is error");
}
// 校验是否已绑定过
Result<Boolean> booleanResult = platformAccountApi.checkUserHasBindGrantType(param.getPlatformUserId(), param.getGrantType());
if(booleanResult==null || booleanResult.getData()){
throw new BusinessException(USER_HAS_BAND_ERROR);
}
GrantTypeEnum grantTypeEnum = GrantTypeEnum.getByCode(param.getGrantType());
if(grantTypeEnum == null){
log.error("grantType is error:{}", param.getGrantType());
throw new BusinessException("grantType is error");
}
switch (Objects.requireNonNull(grantTypeEnum)) {
case SMS_CODE:
return bindService.smsCode(param.getAccount(), param.getPassword(),param.getPlatformUserId());
case ALIPAY:
return bindService.alipay(param.getAccount(),param.getPlatformUserId());
case WECHAT:
return bindService.wechat(param.getAccount(),param.getPlatformUserId());
case MINI_APP:
return bindService.miniApp(param.getAccount(),param.getOthers(),param.getPlatformUserId());
case OFFICIAL_ACCOUNT:
return bindService.officialAccount(param.getAccount(),param.getPlatformUserId());
case USERNAME:
return bindService.username(param.getAccount(), param.getPassword(),param.getPlatformUserId());
case EMAIL:
return bindService.email(param.getAccount(), param.getPassword(),param.getPlatformUserId());
case TWITTER:
return bindService.twitter(param.getAccount(), param.getPassword(),param.getPlatformUserId());
case APPLE_JWT:
return bindService.appleJwt(param.getAccount(),param.getPlatformUserId());
case DEVICE:
return bindService.device(param.getAccount(),param.getPlatformUserId());
case FACEBOOK:
return bindService.facebook(param.getAccount(),param.getPlatformUserId());
case GOOGLE:
return bindService.google(param.getAccount(),param.getPlatformUserId());
default:
log.error("grantType is error:{}", param.getGrantType());
throw new BusinessException("grantType is error");
}
}
private LocalToken generateToken(TokenParam param) {
GrantTypeEnum grantTypeEnum = GrantTypeEnum.getByCode(param.getGrantType());
switch (grantTypeEnum) {
case REFRESH:
return loginService.freshToken(param.getRefreshToken());
case SMS_CODE:
return loginService.smsCode(param.getAccount(), param.getPassword());
case ALIPAY:
return loginService.alipay(param.getAccount());
case WECHAT:
return loginService.wechat(param.getAccount());
case MINI_APP:
return loginService.miniApp(param.getAccount(),param.getOthers());
case OFFICIAL_ACCOUNT:
return loginService.officialAccount(param.getAccount());
case USERNAME:
return loginService.username(param.getAccount(), param.getPassword());
case EMAIL:
return loginService.email(param.getAccount(), param.getPassword());
case TWITTER:
return loginService.twitter(param.getAccount(), param.getPassword());
case APPLE_JWT:
return loginService.appleJwt(param.getAccount());
case DEVICE:
return loginService.device(param.getAccount());
case FACEBOOK:
return loginService.facebook(param.getAccount());
case GOOGLE:
return loginService.google(param.getAccount());
default:
log.error("grantType is error:{}", param.getGrantType());
throw new BusinessException("grantType is error");
}
}
@Override
@Lock(key = "register", spel = "#param.hashCode()")
public LocalToken register(TokenParam param) {
GrantTypeEnum grantTypeEnum = GrantTypeEnum.getByCode(param.getGrantType());
switch (grantTypeEnum) {
case USERNAME:
return registerService.username(param.getAccount(), param.getPassword());
case EMAIL:
return registerService.email(param.getAccount(), param.getPassword());
case TWITTER:
return registerService.twitter(param.getAccount(), param.getPassword());
default:
throw new BusinessException("grantType is error");
}
}
@Override
public void logout(String authorization) {
String accessToken = StringUtils.removeFirst(authorization, "Bearer ");
loginService.logout(accessToken);
}
@Override
public LocalToken validate(String accessToken) {
accessToken = StringUtils.removeFirst(accessToken, "Bearer ");
LocalToken localToken = appRedisTokenStore.readAccessToken(accessToken);
if (localToken == null) {
throw new BusinessException(TOKEN_INVALID_OR_EXPIRED);
}
// Result<PlatformUserInfoDTO> memberInfoDTOResult = platformAccountApi.getPlatformUserInfo(localToken.getMemberId());
// if (!Result.isSuccess(memberInfoDTOResult)) {
// log.error("获取用户信息失败,id:{}", localToken.getMemberId());
// throw new BusinessException(TOKEN_INVALID_OR_EXPIRED);
// }
return localToken;
}
}
@@ -0,0 +1,251 @@
package com.oneone.auth.service.impl;
import cn.hutool.core.util.CharsetUtil;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.oneone.auth.common.ali.AliUtil;
import com.oneone.auth.common.enums.ErrorCodeConstants;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.auth.common.twitter.TwitterUtil;
import com.oneone.auth.common.wechat.MiniAppParam;
import com.oneone.auth.common.wechat.OfficialAccountParam;
import com.oneone.auth.common.wechat.WechatToken;
import com.oneone.auth.common.wechat.WechatUtil;
import com.oneone.auth.entity.dto.AccountInfo;
import com.oneone.auth.service.AccountInfoService;
import com.oneone.auth.service.BindService;
import com.oneone.auth.service.RegisterService;
import com.oneone.auth.util.TokenUtil;
import com.oneone.common.base.LocalToken;
import com.oneone.common.enums.AccountTypeEnum;
import com.oneone.common.exception.BusinessException;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import com.oneone.common.sms.common.SmsTypeEnum;
import com.oneone.common.sms.service.SmsService;
import com.oneone.common.util.Func;
import com.oneone.common.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
@RequiredArgsConstructor
@Slf4j
public class BindServiceImpl implements BindService {
private final AccountInfoService accountInfoService;
private final SmsService smsService;
private final AliUtil aliUtil;
private final WechatUtil wechatUtil;
@Autowired(required = false)
private TwitterUtil twitterUtil;
@Override
public Boolean smsCode(String mobile, String smsCode, Long platformUserId) {
smsService.validate(mobile, smsCode, SmsTypeEnum.LOGIN.getCode());
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.MOBILE.getCode(), mobile);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.MOBILE.getCode(), mobile);
}
@Override
public Boolean username(String username, String password, Long platformUserId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.USERNAME.getCode(), username);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
String encodePassword = MD5Utils.md5Hex(password, CharsetUtil.UTF_8);
return accountInfoService.bind(platformUserId, AccountTypeEnum.USERNAME.getCode(), username,encodePassword);
}
@Override
public Boolean email(String email, String code, Long platformUserId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.EMAIL.getCode(), email);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
String encodePassword = MD5Utils.md5Hex(code, CharsetUtil.UTF_8);
return accountInfoService.bind(platformUserId, AccountTypeEnum.EMAIL.getCode(), email,encodePassword);
}
@Override
public Boolean twitter(String account, String password, Long platformUserId) {
String id = twitterUtil.checkTwitterToken(account, password);
if (Strings.isEmpty(id)) {
throw new BusinessException(ErrorCodeConstants.AUTHORIZED_ERROR);
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.TWITTER.getCode(), id);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.TWITTER.getCode(), id);
}
@Override
public Boolean alipay(String code, Long platformUserId) {
AlipaySystemOauthTokenResponse response = aliUtil.login(code);
String accessToken = response.getAccessToken();
String account = response.getAlipayUserId();
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.ALIPAY.getCode(), account);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.ALIPAY.getCode(), account);
}
@Override
public Boolean wechat(String code, Long platformUserId) {
WechatToken response = wechatUtil.getOauth2AccessToken(code);
String account = response.getOpenId();
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.WECHAT.getCode(), account);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.WECHAT.getCode(), account);
}
@Override
public Boolean miniApp(String code, String miniAppParamStr, Long platformUserId) {
MiniAppParam miniAppParam = null;
if(Func.isNotEmpty(miniAppParamStr)){
miniAppParam = JsonUtils.strToClass(miniAppParamStr, MiniAppParam.class);
}
WechatToken response = wechatUtil.getOauth2AccessTokenByJsCode(code);
String account = response.getOpenId();
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.MINI_APP.getCode(), account);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.MINI_APP.getCode(), account);
}
@Override
public Boolean officialAccount(String officialAccountParamStr, Long platformUserId) {
OfficialAccountParam officialAccountParam = JsonUtils.strToClass(officialAccountParamStr, OfficialAccountParam.class);
WechatToken response = wechatUtil.getOauth2AccessTokenByOfficialAccount(officialAccountParam.getCode());
if (!StringUtils.hasText(response.getOpenId())) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.OFFICIAL_ACCOUNT.getCode(), response.getOpenId());
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.OFFICIAL_ACCOUNT.getCode(), response.getOpenId());
}
@Override
public Boolean appleJwt(String token, Long platformUserId) {
String account = token;
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.APPLE_JWT.getCode(), account);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.APPLE_JWT.getCode(), account);
}
@Override
public Boolean device(String deviceId, Long platformUserId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.DEVICE.getCode(), deviceId);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.DEVICE.getCode(), deviceId);
}
@Override
public Boolean facebook(String facebookUid, Long platformUserId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.FACEBOOK.getCode(), facebookUid);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.FACEBOOK.getCode(), facebookUid);
}
@Override
public Boolean google(String facebookUid, Long platformUserId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.GOOGLE.getCode(), facebookUid);
if (accountInfo != null ) {
if(accountInfo.getPlatformUserId().equals(platformUserId)){
return true;
}
throw new BusinessException(ErrorCodeConstants.GRANT_TYPE_HAS_BAND_ERROR);
}
return accountInfoService.bind(platformUserId, AccountTypeEnum.GOOGLE.getCode(), facebookUid);
}
}
@@ -0,0 +1,378 @@
package com.oneone.auth.service.impl;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.util.CharsetUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.oneone.auth.common.ali.AliUtil;
import com.oneone.auth.common.enums.ErrorCodeConstants;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.auth.common.twitter.TwitterUtil;
import com.oneone.auth.common.wechat.MiniAppParam;
import com.oneone.auth.common.wechat.OfficialAccountParam;
import com.oneone.auth.common.wechat.WechatToken;
import com.oneone.auth.common.wechat.WechatUtil;
import com.oneone.auth.entity.dto.AccountInfo;
import com.oneone.auth.service.AccountInfoService;
import com.oneone.auth.service.LoginService;
import com.oneone.auth.service.RegisterService;
import com.oneone.auth.util.TokenUtil;
import com.oneone.common.base.LocalToken;
import com.oneone.common.enums.AccountTypeEnum;
import com.oneone.common.exception.BusinessException;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import com.oneone.common.result.Result;
import com.oneone.common.result.ResultCode;
import com.oneone.common.sms.common.SmsTypeEnum;
import com.oneone.common.sms.service.SmsService;
import com.oneone.common.util.Func;
import com.oneone.common.util.JsonUtils;
import com.oneone.pms.api.account.vo.RegisterParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.UUID;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 11:40
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class LoginServiceImpl implements LoginService {
private final RegisterService registerService;
private final AccountInfoService accountInfoService;
private final LocalRedisTokenStore appRedisTokenStore;
private final LocalRedisTokenStore miniAppRedisTokenStore;
private final LocalRedisTokenStore officialAccountRedisTokenStore;
private final SmsService smsService;
private final AliUtil aliUtil;
private final WechatUtil wechatUtil;
@Autowired(required = false)
private TwitterUtil twitterUtil;
@Value("${auth.config.tokenValid:86400}")
private Long tokenValid;
@Override
public LocalToken freshToken(String freshToken) {
LocalToken localToken = appRedisTokenStore.readFreshToken(freshToken);
if (localToken == null) {
throw new BusinessException(ErrorCodeConstants.TOKEN_INVALID_OR_EXPIRED);
}
Long platformUserId = localToken.getMemberId();
AccountInfo accountInfo = accountInfoService.getAccountInfo(localToken.getAccountType(), localToken.getAccount());
if (accountInfo != null) {
if (accountInfo.getStatus() == 0) {
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
} else {
throw new BusinessException(ErrorCodeConstants.USER_LOGIN_ERROR);
}
LocalToken token = TokenUtil.generateToken(tokenValid,localToken.getAccountType(), localToken.getAccount(), platformUserId, localToken.getGrantType());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public void logout(String accessToken) {
appRedisTokenStore.removeToken(accessToken);
}
@Override
public LocalToken smsCode(String mobile, String smsCode) {
smsService.validate(mobile, smsCode, SmsTypeEnum.LOGIN.getCode());
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.MOBILE.getCode(), mobile);
if (accountInfo == null) {
return registerService.mobile(mobile,smsCode);
}
if (accountInfo.getStatus() == 0) {
log.error("账号登录,账号被封禁,账号:{}", mobile);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.MOBILE.getCode(), mobile, accountInfo.getPlatformUserId(), GrantTypeEnum.SMS_CODE.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken username(String username, String password) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.USERNAME.getCode(), username);
if (accountInfo == null) {
log.error("账号登录,账号或密码错误,账号:{}", username);
throw new BusinessException(ErrorCodeConstants.USERNAME_OR_PASSWORD_ERROR);
}
String encodePassword = MD5Utils.md5Hex(password, CharsetUtil.UTF_8);
if (!encodePassword.equals(accountInfo.getPassword())) {
log.error("账号登录,账号或密码错误,账号:{}", username);
throw new BusinessException(ErrorCodeConstants.USERNAME_OR_PASSWORD_ERROR);
}
if (accountInfo.getStatus() == 0) {
log.error("账号登录,账号被封禁,账号:{}", username);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.USERNAME.getCode(), username, accountInfo.getPlatformUserId(), GrantTypeEnum.USERNAME.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken email(String email, String code) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.EMAIL.getCode(), email);
if (accountInfo == null) {
log.error("邮件登录,账号或密码错误,账号:{}", email);
throw new BusinessException(ErrorCodeConstants.USERNAME_OR_PASSWORD_ERROR);
}
String encodePassword = MD5Utils.md5Hex(code, CharsetUtil.UTF_8);
if (!encodePassword.equals(accountInfo.getPassword())) {
log.error("邮件登录,账号或密码错误,账号:{}", email);
throw new BusinessException(ErrorCodeConstants.USERNAME_OR_PASSWORD_ERROR);
}
if (accountInfo.getStatus() == 0) {
log.error("邮件登录,账号被封禁,账号:{}", email);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.EMAIL.getCode(), email, accountInfo.getPlatformUserId(), GrantTypeEnum.EMAIL.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken twitter(String account, String password) {
String id = twitterUtil.checkTwitterToken(account, password);
if (Strings.isEmpty(id)) {
throw new BusinessException(ErrorCodeConstants.AUTHORIZED_ERROR);
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.TWITTER.getCode(), id);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.TWITTER.getCode(), account, password);
}
if (accountInfo.getStatus() == 0) {
log.error("twitter登录,账号被封禁,账号:{}", account);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.TWITTER.getCode(), id, accountInfo.getPlatformUserId(), GrantTypeEnum.TWITTER.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken alipay(String code) {
AlipaySystemOauthTokenResponse response = aliUtil.login(code);
String accessToken = response.getAccessToken();
String account = response.getAlipayUserId();
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.ALIPAY.getCode(), account);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.ALIPAY.getCode(), account);
}
if (accountInfo.getStatus() == 0) {
log.error("支付宝登录,账号被封禁,账号:{}", account);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.ALIPAY.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.ALIPAY.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken wechat(String code) {
WechatToken response = wechatUtil.getOauth2AccessToken(code);
String account = response.getOpenId();
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.WECHAT.getCode(), account);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.WECHAT.getCode(), account);
}
if (accountInfo.getStatus() == 0) {
log.error("微信登录,账号被封禁,账号:{}", account);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.WECHAT.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.WECHAT.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken miniApp(String code,String miniAppParamStr) {
MiniAppParam miniAppParam = null;
if(Func.isNotEmpty(miniAppParamStr)){
miniAppParam = JsonUtils.strToClass(miniAppParamStr, MiniAppParam.class);
}
WechatToken response = wechatUtil.getOauth2AccessTokenByJsCode(code);
String account = response.getOpenId();
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.MINI_APP.getCode(), account);
if (accountInfo == null) {
String phoneNumber = null;
if(miniAppParam!=null){
WxMaPhoneNumberInfo wxMaPhoneNumberInfo = wechatUtil.getPhoneNumber(response.getSessionKey(), miniAppParam);
phoneNumber= wxMaPhoneNumberInfo.getPhoneNumber();
}
RegisterParam registerParam=new RegisterParam(AccountTypeEnum.MINI_APP.getCode(), account);
registerParam.setPhone(phoneNumber);
registerParam.setUnionId(response.getUnionId());
accountInfo = accountInfoService.register(registerParam);
}
if (accountInfo.getStatus() == 0) {
log.error("微信登录,账号被封禁,账号:{}", account);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.MINI_APP.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.MINI_APP.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken officialAccount(String officialAccountParamStr) {
OfficialAccountParam officialAccountParam = JsonUtils.strToClass(officialAccountParamStr, OfficialAccountParam.class);
AccountInfo accountInfo = null;
if (StringUtils.hasLength(officialAccountParam.getPhone()) && StringUtils.hasLength(officialAccountParam.getCode())){
smsService.validate(officialAccountParam.getPhone(), officialAccountParam.getCode(), SmsTypeEnum.LOGIN.getCode());
accountInfo = accountInfoService.register(AccountTypeEnum.OFFICIAL_ACCOUNT.getCode(), officialAccountParam.getOpenId(), officialAccountParam.getPhone());
}else {
WechatToken response = wechatUtil.getOauth2AccessTokenByOfficialAccount(officialAccountParam.getCode());
if (!StringUtils.hasText(response.getOpenId())) {
throw new BusinessException("登录失败,请稍后再试");
}
accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.OFFICIAL_ACCOUNT.getCode(), response.getOpenId());
// 为空则返回给前端,前端根据返回的数据判断是否需要绑定手机号
if (accountInfo == null) {
LocalToken localToken = new LocalToken();
localToken.setAccount(response.getOpenId());
return localToken;
}
}
if (accountInfo.getStatus() == 0) {
log.error("微信登录,账号被封禁,账号:{}", accountInfo.getAccount());
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.OFFICIAL_ACCOUNT.getCode(), accountInfo.getAccount(), accountInfo.getPlatformUserId(), GrantTypeEnum.OFFICIAL_ACCOUNT.getCode());
officialAccountRedisTokenStore.storeAccessToken(token);
return token;
}
public LocalToken appleJwt(String token) {
//TODO 前端拿不到token 现在token传的是userId 所以忽略掉验证
//appleOpenid = AppleUtil.validateToken(token);
String account = token;
if (!StringUtils.hasText(account)) {
throw new BusinessException("登录失败,请稍后再试");
}
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.APPLE_JWT.getCode(), account);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.APPLE_JWT.getCode(), account);
}
if (accountInfo.getStatus() == 0) {
log.error("微信登录,账号被封禁,账号:{}", account);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken localToken = TokenUtil.generateToken(tokenValid,AccountTypeEnum.APPLE_JWT.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.APPLE_JWT.getCode());
appRedisTokenStore.storeAccessToken(localToken);
return localToken;
}
@Override
public LocalToken device(String deviceId) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.DEVICE.getCode(), deviceId);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.DEVICE.getCode(), deviceId);
}
if (accountInfo.getStatus() == 0) {
log.error("账号被封禁,账号:{}", deviceId);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.DEVICE.getCode(), deviceId, accountInfo.getPlatformUserId(), GrantTypeEnum.DEVICE.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken facebook(String facebookUid) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.FACEBOOK.getCode(), facebookUid);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.FACEBOOK.getCode(), facebookUid);
}
if (accountInfo.getStatus() == 0) {
log.error("账号被封禁,账号:{}", facebookUid);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.FACEBOOK.getCode(), facebookUid, accountInfo.getPlatformUserId(), GrantTypeEnum.DEVICE.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken google(String facebookUid) {
AccountInfo accountInfo = accountInfoService.getAccountInfo(AccountTypeEnum.GOOGLE.getCode(), facebookUid);
if (accountInfo == null) {
accountInfo = accountInfoService.register(AccountTypeEnum.GOOGLE.getCode(), facebookUid);
}
if (accountInfo.getStatus() == 0) {
log.error("账号被封禁,账号:{}", facebookUid);
throw new BusinessException(ErrorCodeConstants.USER_ACCOUNT_LOCKED);
}
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.GOOGLE.getCode(), facebookUid, accountInfo.getPlatformUserId(), GrantTypeEnum.DEVICE.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
}
@@ -0,0 +1,83 @@
package com.oneone.auth.service.impl;
import com.oneone.auth.common.enums.GrantTypeEnum;
import com.oneone.auth.common.twitter.TwitterUtil;
import com.oneone.auth.entity.dto.AccountInfo;
import com.oneone.auth.service.AccountInfoService;
import com.oneone.auth.service.RegisterService;
import com.oneone.auth.util.TokenUtil;
import com.oneone.common.base.LocalToken;
import com.oneone.common.enums.AccountTypeEnum;
import com.oneone.common.redis.lock.Lock;
import com.oneone.common.redis.utils.LocalRedisTokenStore;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* @author mice
* @version 1.0
* @date 2022-09-21 12:07
*/
@Service
@RequiredArgsConstructor
public class RegisterServiceImpl implements RegisterService {
private final AccountInfoService accountInfoService;
private final LocalRedisTokenStore appRedisTokenStore;
@Autowired(required = false)
private TwitterUtil twitterUtil;
@Value("${auth.config.tokenValid:86400}")
private Long tokenValid;
@Override
public LocalToken mobile(String mobile, String password) {
AccountInfo accountInfo = accountInfoService.register(AccountTypeEnum.MOBILE.getCode(), mobile);
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.MOBILE.getCode(), mobile, accountInfo.getPlatformUserId(), GrantTypeEnum.SMS_CODE.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken username(String account, String password) {
AccountInfo accountInfo = accountInfoService.register(AccountTypeEnum.USERNAME.getCode(), account, password);
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.USERNAME.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.USERNAME.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken email(String account, String password) {
AccountInfo accountInfo = accountInfoService.register(AccountTypeEnum.EMAIL.getCode(), account, password);
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.EMAIL.getCode(), account, accountInfo.getPlatformUserId(), GrantTypeEnum.EMAIL.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
@Override
public LocalToken twitter(String accessToken, String accessTokenSecret) {
String id = twitterUtil.checkTwitterToken(accessToken, accessTokenSecret);
AccountInfo accountInfo = accountInfoService.register(AccountTypeEnum.TWITTER.getCode(), accessToken, accessTokenSecret);
LocalToken token = TokenUtil.generateToken(tokenValid,AccountTypeEnum.TWITTER.getCode(), id, accountInfo.getPlatformUserId(), GrantTypeEnum.TWITTER.getCode());
appRedisTokenStore.storeAccessToken(token);
return token;
}
private LocalToken generateToken(String accountType, String account, Long memberId, String grantType) {
LocalToken token = new LocalToken();
token.setAccessToken(UUID.randomUUID().toString());
token.setRefreshToken(UUID.randomUUID().toString());
token.setAccount(account);
token.setMemberId(memberId);
token.setGrantType(grantType);
token.setAccountType(accountType);
token.setExpiresIn(System.currentTimeMillis() + tokenValid * 1000);
return token;
}
}
@@ -0,0 +1,24 @@
package com.oneone.auth.util;
import com.oneone.common.base.LocalToken;
import com.oneone.upms.api.enums.UserTypeEnum;
import java.util.UUID;
public class TokenUtil {
public static LocalToken generateToken(Long tokenValid,String accountType, String account, Long memberId, String grantType
) {
LocalToken token = new LocalToken();
token.setAccessToken(UUID.randomUUID().toString());
token.setRefreshToken(UUID.randomUUID().toString());
token.setAccount(account);
token.setGrantType(grantType);
token.setAccountType(accountType);
token.setMemberId(memberId);
token.setExpiresIn(System.currentTimeMillis() + tokenValid * 1000);
token.setUserType(UserTypeEnum.MEMBER.getValue());
return token;
}
}
@@ -0,0 +1,21 @@
server:
port: 8000
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
cloud:
nacos:
# 注册中心
discovery:
server-addr: http://192.168.100.151:8848
namespace: d2010a44-0999-4b45-9af8-2a0f028a97c3
# 配置中心
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml
shared-configs[0]:
data-id: oneone-common.yaml
refresh: true
namespace: d2010a44-0999-4b45-9af8-2a0f028a97c3
@@ -0,0 +1,18 @@
server:
port: 8016
spring:
cloud:
nacos:
# 注册中心
discovery:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
#namespace: 8d720841-979d-4a4c-8e26-4dc7345d7b08
# 配置中心
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
#namespace: 8d720841-979d-4a4c-8e26-4dc7345d7b08
file-extension: yaml
shared-configs[0]:
data-id: oneone-common.yaml
refresh: true
@@ -0,0 +1,19 @@
server:
port: 8000
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
cloud:
nacos:
# 注册中心
discovery:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
# 配置中心
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml
shared-configs[0]:
data-id: oneone-common.yaml
refresh: true
@@ -0,0 +1,22 @@
spring:
application:
name: auth-server
main:
allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。
allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务
profiles:
active: dev
springdoc:
api-docs:
enabled: true # 1. 是否开启 Swagger 接文档的元数据
path: /v3/api-docs
swagger-ui:
enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面
path: /swagger-ui.html
default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档
knife4j:
enable: true # 2.2 是否开启 Swagger 文档的 Knife4j UI 界面
setting:
language: zh_cn