3 Commits

Author SHA1 Message Date
lcry
5a1523564b !677 add 新增 对接 gitea 三方单点登录
* add 新增 对接 gitea 三方单点登录
2025-05-12 02:07:46 +00:00
AprilWind
0c2fe34d92 !678 add 新增自定义 Date 类型反序列化处理器(支持多种格式)
* add 新增自定义 Date 类型反序列化处理器(支持多种格式)
2025-05-12 02:04:43 +00:00
疯狂的狮子Li
2dde42168f update 更新 readme 增加新成员项目 2025-05-12 09:33:32 +08:00
13 changed files with 209 additions and 10 deletions

View File

@@ -24,6 +24,7 @@
> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br> > 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5) > 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)
> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)
> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网) > 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网)

View File

@@ -263,3 +263,9 @@ justauth:
client-id: 10**********6 client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea

View File

@@ -265,3 +265,9 @@ justauth:
client-id: 10**********6 client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea

View File

@@ -1,11 +1,13 @@
package org.dromara.common.json.config; package org.dromara.common.json.config;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.dromara.common.json.handler.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.handler.BigNumberSerializer;
import org.dromara.common.json.handler.CustomDateDeserializer;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
@@ -15,6 +17,7 @@ import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
@@ -38,7 +41,10 @@ public class JacksonConfig {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter)); javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
builder.modules(javaTimeModule); // 自定义 java.util.Date 的反序列化(支持多格式字符串解析)
SimpleModule dateModule = new SimpleModule();
dateModule.addDeserializer(Date.class, new CustomDateDeserializer());
builder.modules(javaTimeModule, dateModule);
builder.timeZone(TimeZone.getDefault()); builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置"); log.info("初始化 jackson 配置");
}; };

View File

@@ -0,0 +1,31 @@
package org.dromara.common.json.handler;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import java.util.Date;
/**
* 自定义 Date 类型反序列化处理器(支持多种格式)
*
* @author AprilWind
*/
public class CustomDateDeserializer extends JsonDeserializer<Date> {
/**
* 反序列化逻辑:将字符串转换为 Date 对象
*
* @param p JSON 解析器,用于获取字符串值
* @param ctxt 上下文环境(可用于获取更多配置)
* @return 转换后的 Date 对象,若为空字符串返回 null
* @throws IOException 当字符串格式非法或转换失败时抛出
*/
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return DateUtil.parse(p.getText());
}
}

View File

@@ -0,0 +1,92 @@
package org.dromara.common.social.gitea;
import cn.hutool.core.lang.Dict;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.utils.JsonUtils;
/**
* @author lcry
*/
@Slf4j
public class AuthGiteaRequest extends AuthDefaultRequest {
public static final String SERVER_URL = SpringUtils.getProperty("justauth.type.gitea.server-url");
/**
* 设定归属域
*/
public AuthGiteaRequest(AuthConfig config) {
super(config, AuthGiteaSource.GITEA);
}
public AuthGiteaRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthGiteaSource.GITEA, authStateCache);
}
@Override
public AuthToken getAccessToken(AuthCallback authCallback) {
String body = doPostAuthorizationCode(authCallback.getCode());
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthToken.builder()
.accessToken(object.getStr("access_token"))
.refreshToken(object.getStr("refresh_token"))
.idToken(object.getStr("id_token"))
.tokenType(object.getStr("token_type"))
.scope(object.getStr("scope"))
.build();
}
@Override
protected String doPostAuthorizationCode(String code) {
HttpRequest request = HttpRequest.post(source.accessToken())
.form("client_id", config.getClientId())
.form("client_secret", config.getClientSecret())
.form("grant_type", "authorization_code")
.form("code", code)
.form("redirect_uri", config.getRedirectUri());
HttpResponse response = request.execute();
return response.body();
}
@Override
public AuthUser getUserInfo(AuthToken authToken) {
String body = doGetUserInfo(authToken);
Dict object = JsonUtils.parseMap(body);
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getStr("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getStr("message"));
}
return AuthUser.builder()
.uuid(object.getStr("sub"))
.username(object.getStr("name"))
.nickname(object.getStr("preferred_username"))
.avatar(object.getStr("picture"))
.email(object.getStr("email"))
.token(authToken)
.source(source.toString())
.build();
}
}

View File

@@ -0,0 +1,50 @@
package org.dromara.common.social.gitea;
import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.request.AuthDefaultRequest;
/**
* gitea Oauth2 默认接口说明
*
* @author lcry
*/
public enum AuthGiteaSource implements AuthSource {
/**
* 自己搭建的 gitea 私服
*/
GITEA {
/**
* 授权的api
*/
@Override
public String authorize() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/authorize";
}
/**
* 获取accessToken的api
*/
@Override
public String accessToken() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/access_token";
}
/**
* 获取用户信息的api
*/
@Override
public String userInfo() {
return AuthGiteaRequest.SERVER_URL + "/login/oauth/userinfo";
}
/**
* 平台对应的 AuthRequest 实现类,必须继承自 {@link AuthDefaultRequest}
*/
@Override
public Class<? extends AuthDefaultRequest> getTargetClass() {
return AuthGiteaRequest.class;
}
}
}

View File

@@ -10,6 +10,7 @@ import me.zhyd.oauth.request.*;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.gitea.AuthGiteaRequest;
import org.dromara.common.social.maxkey.AuthMaxKeyRequest; import org.dromara.common.social.maxkey.AuthMaxKeyRequest;
import org.dromara.common.social.topiam.AuthTopIamRequest; import org.dromara.common.social.topiam.AuthTopIamRequest;
@@ -66,6 +67,7 @@ public class SocialUtils {
case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE); case "aliyun" -> new AuthAliyunRequest(builder.build(), STATE_CACHE);
case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE); case "maxkey" -> new AuthMaxKeyRequest(builder.build(), STATE_CACHE);
case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE); case "topiam" -> new AuthTopIamRequest(builder.build(), STATE_CACHE);
case "gitea" -> new AuthGiteaRequest(builder.build(), STATE_CACHE);
default -> throw new AuthException("未获取到有效的Auth配置"); default -> throw new AuthException("未获取到有效的Auth配置");
}; };
} }

View File

@@ -13,9 +13,9 @@ create table sys_social
nick_name varchar2(30) default '', nick_name varchar2(30) default '',
email varchar2(255) default '', email varchar2(255) default '',
avatar varchar2(500) default '', avatar varchar2(500) default '',
access_token varchar2(255) not null, access_token varchar2(2000) not null,
expire_in number(20) default null, expire_in number(20) default null,
refresh_token varchar2(255) default null, refresh_token varchar2(2000) default null,
access_code varchar2(255) default null, access_code varchar2(255) default null,
union_id varchar2(255) default null, union_id varchar2(255) default null,
scope varchar2(255) default null, scope varchar2(255) default null,

View File

@@ -13,9 +13,9 @@ create table sys_social
nick_name varchar(30) default ''::varchar, nick_name varchar(30) default ''::varchar,
email varchar(255) default ''::varchar, email varchar(255) default ''::varchar,
avatar varchar(500) default ''::varchar, avatar varchar(500) default ''::varchar,
access_token varchar(255) not null, access_token varchar(2000) not null,
expire_in int8 default null, expire_in int8 default null,
refresh_token varchar(255) default null::varchar, refresh_token varchar(2000) default null::varchar,
access_code varchar(255) default null::varchar, access_code varchar(255) default null::varchar,
union_id varchar(255) default null::varchar, union_id varchar(255) default null::varchar,
scope varchar(255) default null::varchar, scope varchar(255) default null::varchar,

View File

@@ -13,10 +13,10 @@ create table sys_social
nick_name varchar(30) default '' comment '用户昵称', nick_name varchar(30) default '' comment '用户昵称',
email varchar(255) default '' comment '用户邮箱', email varchar(255) default '' comment '用户邮箱',
avatar varchar(500) default '' comment '头像地址', avatar varchar(500) default '' comment '头像地址',
access_token varchar(255) not null comment '用户的授权令牌', access_token varchar(2000) not null comment '用户的授权令牌',
expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有', expire_in int default null comment '用户的授权令牌的有效期,部分平台可能没有',
refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有', refresh_token varchar(255) default null comment '刷新令牌,部分平台可能没有',
access_code varchar(255) default null comment '平台的授权信息,部分平台可能没有', access_code varchar(2000) default null comment '平台的授权信息,部分平台可能没有',
union_id varchar(255) default null comment '用户的 unionid', union_id varchar(255) default null comment '用户的 unionid',
scope varchar(255) default null comment '授予的权限,部分平台可能没有', scope varchar(255) default null comment '授予的权限,部分平台可能没有',
token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有', token_type varchar(255) default null comment '个别平台的授权信息,部分平台可能没有',

View File

@@ -10,9 +10,9 @@ create table sys_social
nick_name nvarchar(30) DEFAULT ('') NULL, nick_name nvarchar(30) DEFAULT ('') NULL,
email nvarchar(255) DEFAULT ('') NULL, email nvarchar(255) DEFAULT ('') NULL,
avatar nvarchar(500) DEFAULT ('') NULL, avatar nvarchar(500) DEFAULT ('') NULL,
access_token nvarchar(255) NOT NULL, access_token nvarchar(2000) NOT NULL,
expire_in bigint NULL, expire_in bigint NULL,
refresh_token nvarchar(255) NULL, refresh_token nvarchar(2000) NULL,
access_code nvarchar(255) NULL, access_code nvarchar(255) NULL,
union_id nvarchar(255) NULL, union_id nvarchar(255) NULL,
scope nvarchar(255) NULL, scope nvarchar(255) NULL,

View File

@@ -6,3 +6,8 @@ ALTER TABLE `flow_instance`
ALTER TABLE `flow_his_task` ALTER TABLE `flow_his_task`
MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回' AFTER `skip_type` MODIFY COLUMN `flow_status` varchar(20) NOT NULL COMMENT '流程状态0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回' AFTER `skip_type`
ALTER TABLE `sys_social`
MODIFY COLUMN `access_token` varchar(2000) NOT NULL COMMENT '用户的授权令牌' AFTER `avatar`;
ALTER TABLE `sys_social`
MODIFY COLUMN `refresh_token` varchar(2000) NOT NULL COMMENT '刷新令牌,部分平台可能没有' AFTER `expire_in`;