提交 1539703b authored 作者: husishuai's avatar husishuai

init

上级
# gitlab-sso-auth2
本工程项目 ,主要用于针对基地AUAP统一认证中心做集成认证,并提供标准的OAUTH2对接协议,实现于gitlab的认证集成实现
## api接口说明
### GET /oauth2/authorize
> 此接口用于进行授权认证请求,获取AUAP的统一认证中心认证用户后,生成对应的授权码,重定向到gitlab的Generic OAuth2客户端进行认证
- 请求格式:
### POST /oauth2/token
> 此接口用于进行通过授权码code,生成access_token,返回OAUTH2标准的响应格式
- 请求格式(form-data):
| 参数中文名称 | 参数名 | 类型定义 | 必填 | 说明 |
| ------ | ------- | ------ | -- | ------------------------------------------------------------------------- |
| 应用ID | client_id | String | 是 | 平台提供 |
| 应用秘钥 | client_secret | String | 是 | 平台提供 |
| 应用地址 | redirect_uri | String | 是 | 应用认证callback地址。|
| 授权code | code | String | 是 | 通过/oauth2/authorize获取 |
- 响应格式:
```json
{
"access_token": "",
"refresh_token": "",
"scope": null,
"id_token": null,
"token_type": "Bearer",
"expires_in": 1713872479809
}
```
### GET userinfo
> 此接口用于通过access_token获取认证用户信息,并返回认证用户信息给gitlab
- 请求格式(header):
| 参数中文名称 | 参数名 | 类型定义 | 必填 | 说明 |
| ------ | ------- | ------ | -- | ------------------------------------------------------------------------- |
| 授权参数 | Authorization | String | 是 | Bearer {{access_token}} |
- 响应格式:
```json
{
"code": "200",
"msg": null,
"data": {
"userId": "1",
"orgName": "",
"realname": "",
"enName": "",
"cardNum": "",
"email": "test@qq.com",
"username": "test"
},
"ts": 1713843759731,
"success": true
}
```
## gitlab集成Generic OAuth2客户端
### 客户端gitlab的配置,主要修改 /etc/gitlab/gitlab.rb
配置的含义可以看gitlab官方解释,具体配置app\_id和app\_secret 自己和服务端关联起来就好,三个接口可以自己定义,gitlab有默认接口。
- 官方接口文档
<https://docs.gitlab.com/ee/integration/omniauth.html?tab=Linux+package+%28Omnibus%29>
<https://docs.gitlab.com/ee/integration/oauth2_generic.html>
- 参考博文
<https://gitlab.cn/blog/2022/03/03/jihu-sso/>
<https://blog.csdn.net/qq_16077549/article/details/118720698?share_token=35f09782-6330-4714-b52d-bcbfbddc1111>
<https://blog.csdn.net/qq_41528213/article/details/105205564>
### 修改/etc/gitlab/gitlab.rb
> 注意:在配置过程中,需要注意id_path的配置,这里需要逐层获取对应的属性,不然会出现认证成功后,一直登录的是第一次创建的用户信息
```
#OAuth2.0
gitlab_rails['omniauth_enabled'] = true
# 创建一个GitLab帐户,然后通过配置文件设置将其连接到您的OmniAuth提供者帐户。值需要和omniauth_providers参数中的name值保持一致
gitlab_rails['omniauth_allow_single_sign_on'] = ['oauth2_generic']
# 值为false:使用OAuth登录的用户无需管理员审批,自动创建GitLab用户,值为true:使用OAuth登录自动创建的自动创建GitLab用户需要管理员审批
gitlab_rails['omniauth_block_auto_created_users'] = true
# 值为true:gitlab未登录的情况访问,gitlab会自动跳转到oauth2_generic对应的认证登录界面,如果要访问默认的登录界面,需在gitlab的默认登录页面地址后添加?auto_sign_in=false参数,示例:http://192.168.1.126:9121/users/sign_in?auto_sign_in=false
gitlab_rails['omniauth_auto_sign_in_with_provider'] = 'oauth2_generic'
gitlab_rails['omniauth_auto_link_user'] = ["oauth2_generic"]
gitlab_rails['omniauth_auto_link_oauth2_generic_user'] = true
gitlab_rails['omniauth_providers'] = [
{
'name' => 'oauth2_generic',
# 显示在登录界面上的认证名称
'label' => '中科知用统一认证中心',
# 认证服务器创建的应用ID
'app_id' => '1759151517098573824',
# 认证服务器创建的应用的秘钥
'app_secret' => 'ef75da89794e424d9936df3573d8b0c7',
'args' => {
client_options: {
# OAuth SSO 登录认证URL
'site' => 'http://scjoyedu.eicp.net:58000',
# OAuth 各服务的URL
'authorize_url' => '/auth/oauth2/authorize',
'token_url' => '/auth/oauth2/token',
'user_info_url' => '/auth/userinfo'
},
# 携带oauth2授权认证的scope的参数
authorize_params: {
scope: "openid"
},
# 定义user_info_url获取到认证用户信息的结构体
user_response_structure: {
# root_path用于逐层解析用户信息的Json,{data:{userId:'',realname:'',email:'',enName:''}}
root_path: ["data"],
# id_path是相对于root_path节点下的某个属性,作为GitLab用户的唯一id,这里配置的含义就是读取用户信息的Json中data属性下的email属性作为唯一id,对应gitlab用户的Identifier
id_path: ["data","email"],
# attributes是将root_path节点下的各个属性映射为标准Omniauth的用户属性,具体见 https://github.com/omniauth/omniauth/wiki/auth-hash-schema#schema-10-and-later
attributes: {
nickname: 'userId',
name: 'realname',
email: 'email',
username:'enName'
}
},
strategy_class: "OmniAuth::Strategies::OAuth2Generic"
}
}
]
```
### 更新gitlab配置和重启gitlab
```
# 修改了/etc/gitlab/gitlab.rb文件,需要执行以下命令后,配置才会生效
gitlab-ctl reconfigure
# 重启gitlab
gitlab-ctl restart
```
\ No newline at end of file
<?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>gitlab-sso</artifactId>
<groupId>com.gitlab</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gitlab-sso-oauth2</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--开启自定义 配置 需要提示功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-models-jakarta</artifactId>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
</dependency>
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>gitlab-sso-oauth2</finalName>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/**.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.1.5</version>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<imageName>app/gitlab-sso-oauth2</imageName>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
FROM openjdk:17-oracle
VOLUME /tmp
ADD gitlab-sso-oauth2.jar app.jar
ADD docker-startup.sh docker-startup.sh
RUN sh -c 'touch /app.jar'
RUN sh -c 'chmod +x /docker-startup.sh'
ENV JAVA_OPTS=""
ENV ENV_OPTS=""
ENTRYPOINT [ "sh", "-c", "/docker-startup.sh" ]
\ No newline at end of file
version: "3"
services:
gitlab-sso-oauth2:
container_name: gitlab-sso-oauth2-dev
image: app/gitlab-sso-oauth2
restart: always
network_mode: "host"
environment:
- TZ=Asia/Shanghai
- NACOS_SERVER_IP=192.168.1.171:8848
- NACOS_NAMESPACE=f750253a-a295-4148-a2fd-63ee647d4880
- ENV_OPTS=--spring.profiles.active=dev --spring.cloud.nacos.discovery.enabled=true --spring.cloud.nacos.config.enabled=true
- JAVA_OPTS=-server -Xmx512m -Xms512m -Xss256k -XX:-UseGCOverheadLimit -XX:SurvivorRatio=8 -Duser.timezone=GMT+08 -DLogDir=/data/dfs/env/bin/logs/app
volumes:
- /etc/localtime:/etc/localtime
- /data/dfs/env:/data/dfs/env
version: "3"
services:
gitlab-sso-oauth2:
container_name: gitlab-sso-oauth2
image: app/gitlab-sso-oauth2
restart: always
ports:
- "8281:8281"
environment:
- TZ=Asia/Shanghai
- NACOS_SERVER_IP=192.168.1.171:8848
- NACOS_NAMESPACE=46df3807-70d2-43c1-8785-91cc30ca6057
- ENV_OPTS=--spring.profiles.active=prod --spring.cloud.nacos.discovery.enabled=true --spring.cloud.nacos.config.enabled=true
- JAVA_OPTS=-server -Xmx512m -Xms512m -Xss256k -XX:-UseGCOverheadLimit -XX:SurvivorRatio=8 -Duser.timezone=GMT+08 -DLogDir=/data/dfs/env/bin/logs/app
volumes:
- /etc/localtime:/etc/localtime
- /data/dfs/env:/data/dfs/env
version: "3"
services:
gitlab-sso-oauth2:
container_name: gitlab-sso-oauth2
image: app/gitlab-sso-oauth2
restart: always
ports:
- "8282:8282"
environment:
- TZ=Asia/Shanghai
- NACOS_SERVER_IP=192.168.1.171:8848
- NACOS_NAMESPACE=f750253a-a295-4148-a2fd-63ee647d4880
- ENV_OPTS=--spring.profiles.active=release --spring.cloud.nacos.discovery.enabled=true --spring.cloud.nacos.config.enabled=true
- JAVA_OPTS=-server -Xmx512m -Xms512m -Xss256k -XX:-UseGCOverheadLimit -XX:SurvivorRatio=8 -Duser.timezone=GMT+08 -DLogDir=/data/dfs/env/bin/logs/app
volumes:
- /etc/localtime:/etc/localtime
- /data/dfs/env:/data/dfs/env
version: "3"
services:
gitlab-sso-oauth2:
container_name: gitlab-sso-oauth2-test
image: app/gitlab-sso-oauth2
restart: always
network_mode: "host"
environment:
- TZ=Asia/Shanghai
- NACOS_SERVER_IP=192.168.1.171:8848
- NACOS_NAMESPACE=51055752-b113-449a-90b9-8faffbffa538
- ENV_OPTS=--spring.profiles.active=test --spring.cloud.nacos.discovery.enabled=true --spring.cloud.nacos.config.enabled=true
- JAVA_OPTS=-server -Xmx2048m -Xms2048m -Xss512k -XX:-UseGCOverheadLimit -XX:SurvivorRatio=8 -Duser.timezone=GMT+08 -DLogDir=/data/dfs/env/bin/logs/app
volumes:
- /etc/localtime:/etc/localtime
- /data/dfs/env:/data/dfs/env
#!/bin/bash
#===========================================================================================
# JVM Configuration
#===========================================================================================
JAVA_OPTS="${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom"
if [[ "${NACOS_DEBUG}" == "y" ]]; then
JAVA_OPTS="${JAVA_OPTS} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPTS="${JAVA_OPTS} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${BASE_DIR}/logs/java_heapdump.hprof"
JAVA_OPTS="${JAVA_OPTS} -XX:-UseLargePages"
fi
#===========================================================================================
# Setting system properties
#===========================================================================================
# set nacos server ip
ENV_OPTS="${ENV_OPTS} --server.max-http-header-size=524288"
if [[ ! -z "${NACOS_SERVER_IP}" ]]; then
ENV_OPTS="${ENV_OPTS} --NACOS_SERVER_IP=${NACOS_SERVER_IP}"
fi
if [[ ! -z "${NACOS_NAMESPACE}" ]]; then
ENV_OPTS="${ENV_OPTS} --NACOS_NAMESPACE=${NACOS_NAMESPACE}"
fi
JAVA_OPTS="${JAVA_OPTS} -jar /app.jar ${ENV_OPTS}"
echo "app is starting, you can docker logs your container"
exec java ${JAVA_OPTS}
#!/usr/bin/env bash
value=$1
path=$2
forceparam=$3
force=${forceparam:-"true"}
force_update="-U"
if [ $force == false ]
then
force_update=""
fi
active=${value:-""}
composeFilePath=${path:-""}
app_name="gitlab-sso-oauth2"
image_name="app/${app_name}"
if [ $active == "" ]
then
app_name=$app_name
else
app_name="${app_name}-${active}"
fi
docker stop ${app_name}
docker rm ${app_name}
docker rmi ${image_name}
cd ../../../
mvn clean install -Dmaven.test.skip=true $force_update
mvn docker:build
cd target/docker
if [ $active == "" ]
then
docker-compose up -d
else
docker-compose -f ${composeFilePath}docker-compose-${active}.yaml up -d
fi
package com.gitlab;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description Oauth2Application 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@SpringBootApplication
public class Oauth2Application {
public static void main(String[] args) {
double start = (double)System.currentTimeMillis();
SpringApplication.run(Oauth2Application.class, args);
double end = (double)System.currentTimeMillis();
System.out.println("==============================系统启动成功 启动耗时:" + (end - start) / 1000 + "秒==============================");
}
}
package com.gitlab.config;
import com.gitlab.util.OAuth2SecurityUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description OAuth2Config 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Configuration
@EnableConfigurationProperties(OAuth2Properties.class)
public class OAuth2Config {
@Bean
public OAuth2SecurityUtils securityUtils(OAuth2Properties oAuth2Properties){
return new OAuth2SecurityUtils(oAuth2Properties);
}
}
package com.gitlab.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Description OAuth2Properties 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Data
@ConfigurationProperties(prefix = "oauth2")
public class OAuth2Properties {
/**
* client_id
*/
private String client_id;
/**
* client_secret
*/
private String client_secret;
/**
* 重定向地址
*/
private String redirect_uri;
/**
* 签名KEY
*/
private String key;
/**
* 签名加密公钥
*/
private String singlePublicKey;
/**
* 签名解密私钥
*/
private String singlePrivateKey;
/**
* code超时时间,单位秒
*/
private Integer codeExpires;
/**
* accessToken超时时间,单位秒
*/
private Integer tokenExpires;
}
package com.gitlab.constant;
/**
* @Description OAuth2Constants 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
public class OAuth2Constants {
public final static String HEADER_AUTHORIZATION_NAME = "Authorization";
public final static String HEADER_AUTHORIZATION_BEARER = "Bearer ";
}
package com.gitlab.controller;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSONObject;
import com.gitlab.config.OAuth2Properties;
import com.gitlab.constant.OAuth2Constants;
import com.gitlab.exception.BizException;
import com.gitlab.model.*;
import com.gitlab.util.JWTUtils;
import com.gitlab.util.OAuth2SecurityUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Description OAuth2Controller 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Slf4j
@Tag(name = "OAuth2认证服务")
@Controller
public class OAuth2Controller {
@Autowired
private OAuth2SecurityUtils securityUtils;
@Autowired
private OAuth2Properties properties;
@Value(value = "${oauth2.redirect_login}")
private String redirectLogin;
@RequestMapping("/oauth2/authorize")
@Operation(summary = "认证授权")
public String authorize(HttpServletRequest request, AuthorizeRequestDTO requestDTO) {
UserinfoDTO userinfoDTO = this.getUserinfo(request);
if (BeanUtil.isEmpty(userinfoDTO)) {
return String.format("redirect:%s", redirectLogin);
}
String data = JSONObject.toJSONString(userinfoDTO);
try {
data = securityUtils.encryptByPublicKey(data);
// 计算code超时时间
DateTime dateTime = DateUtil.offset(new Date(), DateField.SECOND, this.properties.getCodeExpires());
String code = JWTUtils.create(this.properties.getSinglePrivateKey(), userinfoDTO.getUsername(), data, dateTime.getTime());
String redirect_uri = requestDTO.getRedirect_uri();
String redirect = "";
if (redirect_uri.contains("?")) {
redirect = String.format("%s&code=%s", redirect_uri, code);
}
else {
redirect = String.format("%s?code=%s", redirect_uri, code);
}
if(StringUtils.hasLength(requestDTO.getState())){
redirect = String.format("%s&state=%s", redirect, requestDTO.getState());
}
return String.format("redirect:%s", redirect);
} catch (Exception e) {
e.printStackTrace();
throw new BizException("认证授权异常!");
}
// auap开发逻辑
/**
* 认证授权接口地址:/auth/oauth2/authorize
* 请求参数:
*/
}
@ResponseBody
@PostMapping("/oauth2/token")
@Operation(summary = "获取AccessToken")
public AccessTokenDTO token(TokenRequestDTO requestDTO) {
if (!StringUtils.hasLength(requestDTO.getClient_id())) {
throw new BizException("client_id is null");
}
if (!StringUtils.hasLength(requestDTO.getClient_secret())) {
throw new BizException("client_secret is null");
}
if (!StringUtils.hasLength(requestDTO.getCode())) {
throw new BizException("code is null");
}
AccessTokenDTO tokenDTO = new AccessTokenDTO();
try {
if (!JWTUtils.verify(requestDTO.getCode(), this.properties.getSinglePrivateKey())) {
throw new BizException("认证失败,凭证非法,请重新认证!");
}
AtomicReference<Long> expire = new AtomicReference<>();
AtomicReference<String> sub = new AtomicReference<>();
String data = (String) JWTUtils.of(requestDTO.getCode(), jwt -> {
expire.set((Long) jwt.getPayload("exp"));
sub.set((String) jwt.getPayload("sub"));
return jwt.getPayload("data");
});
if (expire.get().compareTo(System.currentTimeMillis()) < 0) {
throw new BizException("认证失败,凭证过期,请重新认证!");
}
// 计算token超时时间
DateTime accessTokenDateTime = DateUtil.offset(new Date(), DateField.SECOND, this.properties.getTokenExpires());
DateTime refreshTokenDateTime = DateUtil.offset(new Date(), DateField.SECOND, this.properties.getTokenExpires() + this.properties.getTokenExpires());
String accessToken = JWTUtils.create(this.properties.getSinglePrivateKey(), sub.get(), data, accessTokenDateTime.getTime());
String refreshToken = JWTUtils.create(this.properties.getSinglePrivateKey(), sub.get(), data, refreshTokenDateTime.getTime());
tokenDTO = new AccessTokenDTO()
.setAccess_token(accessToken)
.setRefresh_token(refreshToken)
.setId_token(accessToken)
.setToken_type(OAuth2Constants.HEADER_AUTHORIZATION_BEARER.replace(" ", ""))
.setExpires_in(DateUtil.offset(new Date(), DateField.HOUR, 8).getTime());
} catch (Exception e) {
e.printStackTrace();
throw new BizException(e.getMessage(),e.getCause());
}
return tokenDTO;
}
@ResponseBody
@RequestMapping("/userinfo")
@Operation(summary = "获取userinfo用户信息")
public UserinfoDTO profile(HttpServletRequest request) {
String access_token = request.getHeader(OAuth2Constants.HEADER_AUTHORIZATION_NAME);
if (!StringUtils.hasLength(access_token)) {
log.error("认证失败,参数为空!");
return null;
}
try {
access_token = access_token.replace(OAuth2Constants.HEADER_AUTHORIZATION_BEARER, "");
if (!JWTUtils.verify(access_token, this.properties.getSinglePrivateKey())) {
throw new BizException("认证失败,凭证非法,请重新认证!");
}
AtomicReference<Long> expire = new AtomicReference<>();
UserinfoDTO userinfoDTO = JWTUtils.of(access_token, jwt -> {
expire.set((Long) jwt.getPayload("exp"));
String data = (String) jwt.getPayload("data");
try {
String decodeValue = this.securityUtils.decryptByPrivateKey(data);
if (!StringUtils.hasLength(decodeValue) || !(decodeValue.startsWith("{") && decodeValue.endsWith("}"))) {
return null;
}
return JSONObject.parseObject(decodeValue, UserinfoDTO.class);
} catch (Exception e) {
e.printStackTrace();
return null;
}
});
if (Objects.isNull(userinfoDTO)) {
throw new BizException("认证失败,参数非法!");
}
if (expire.get().compareTo(System.currentTimeMillis()) < 0) {
throw new BizException("认证失败,凭证过期,请重新认证!");
}
// userinfoDTO.setEmail(userinfoDTO.getUsername());
return userinfoDTO;
} catch (Exception e) {
e.printStackTrace();
log.error("认证失败,系统出现异常,请联系系统管理员");
return null;
}
}
private UserinfoDTO getUserinfo(HttpServletRequest request) {
UserinfoDTO userinfoDTO = new UserinfoDTO();
userinfoDTO.setUserId("2");
userinfoDTO.setRealname("张三");
// 该字段jfrog使用(但是传了email后该字段的值会失效,填充的内容将是email)
userinfoDTO.setUsername("zhangsan");
userinfoDTO.setEnName("zhangsan");
userinfoDTO.setEmail("365152541@qq.com");
return userinfoDTO;
}
public static void main(String[] args) {
System.out.println(Base64.getEncoder().encodeToString("ssoAdmin:Admin@123".getBytes()));
}
}
package com.gitlab.enums;
import com.gitlab.exception.ErrorCode;
import lombok.Getter;
/**
* @Description BasicErrorCode 类(或接口)
* @Author huangshun
* @Date 2023/6/17
*/
@Getter
public enum BasicErrorCode implements ErrorCode {
// ============400系列============
BAD_REQUEST("400", "请求的数据格式不符!"),
NOT_AUTH("401", "登录凭证过期!"),
NOT_PERMISSION("403", "抱歉,你无权限访问!"),
NOT_FOUND("404", "请求的资源找不到!"),
// ============500系列============
SERVER_ERROR("500", "服务器内部错误!"),
SERVICE_UNAVAILABLE("503", "服务器正忙,请稍后再试!"),
// ============token错误3000-3999============
TOKEN_INVALID("3002", "无效的登录凭证"),
TOKEN_SIGNATURE_INVALID("3003", "无效的签名"),
TOKEN_EXPIRED("3004", "登录凭证已过期"),
TOKEN_MISSION("3005", "登录凭证缺失"),
TOKEN_REFRESH_INVALID("3006", "Token刷新无效"),
// ============错误类型E,错误来源于当前系统,用于错误问题反馈,预留编码范围:4000-6000============
UNKNOWN_ERROR("E5000", "服务器出了点小差"),
PARAM_ERROR("E4001", "参数错误"),
NULL_POINTER("E4002", "空指针错误"),
HTTP_ERROR("E4003", "HTTP错误")
// ============警告类型W,错误来源于当前系统,用于问题警告,预留编码范围:4000-6000============
// ============提醒类型N,错误来源于当前系统,用于业务提醒,预留编码范围:4000-6000============
;
private String code;
private String message;
BasicErrorCode(String code, String message) {
this.code = code;
this.message = message;
}
}
package com.gitlab.enums;
/**
* @Description ResultEnum 类(或接口)
* @Author huangshun
* @Date 2023/6/17
*/
public enum ResultEnum {
// ============成功状态码200============
SUCCESS(true, "200", "成功");
/**
* 响应是否成功
*/
private Boolean success;
/**
* 响应状态码
*/
private String code;
/**
* 响应信息
*/
private String message;
ResultEnum(boolean success, String code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public Boolean getSuccess() {
return success;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
package com.gitlab.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Objects;
/**
* @Description BaseException 类(或接口)
* @Author huangshun
* @Date 2023/6/17
*/
@EqualsAndHashCode(callSuper = true)
@Data
public abstract class BaseException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
private ErrorCode errorCode;
/**
* 方法名称
*/
private String method;
public BaseException() {
}
public BaseException(String message, Throwable e) {
super(message, e);
}
public BaseException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
public BaseException(Throwable cause) {
super(cause);
}
public BaseException(String message) {
super(message);
}
public BaseException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public BaseException(ErrorCode errorCode, String message, String method) {
super(message);
this.errorCode = errorCode;
this.method = method;
}
public ErrorCode getErrorCode() {
return errorCode;
}
public void setErrorCode(ErrorCode errorCode) {
this.errorCode = errorCode;
}
public String getCode() {
return Objects.nonNull(this.errorCode) ? this.errorCode.getCode() : null;
}
}
package com.gitlab.exception;
import com.gitlab.enums.BasicErrorCode;
/**
* @Description BizException 通用业务异常定义
* @Author huangshun
* @Date 2023/6/18
*/
public class BizException extends BaseException {
private static final long serialVersionUID = 1L;
public BizException(String message) {
super(message);
this.setErrorCode(BasicErrorCode.UNKNOWN_ERROR);
}
public BizException(ErrorCode errorCode, String message) {
super(message);
this.setErrorCode(errorCode);
}
public BizException(String message,Throwable e) {
super(message,e);
this.setErrorCode(BasicErrorCode.UNKNOWN_ERROR);
}
}
package com.gitlab.exception;
/**
* @Description BaseErrorCode 类(或接口)
* @Author huangshun
* @Date 2023/6/17
*/
public interface ErrorCode {
/**
* 错误代码
*/
public String getCode();
/**
* 错误信息
*/
public String getMessage();
}
package com.gitlab.exception;
import cn.hutool.core.util.StrUtil;
import com.gitlab.enums.BasicErrorCode;
import com.gitlab.model.ResultEntity;
import com.gitlab.util.ServletUtils;
import com.gitlab.util.ValidatorUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;
import java.util.Objects;
import java.util.Set;
/**
* @Description GlobalExceptionHandler 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 400 - Bad Request
*/
@ExceptionHandler(value = {MethodArgumentNotValidException.class,
BindException.class,
HttpMessageNotReadableException.class,
ConstraintViolationException.class,
ValidationException.class
})
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> handleMethodArgumentNotValidException(Exception e) {
log.error("参数验证失败:", e);
String message;
if (e instanceof MethodArgumentNotValidException || e instanceof BindException) {
BindingResult result;
if (e instanceof MethodArgumentNotValidException) {
result = ((MethodArgumentNotValidException) e).getBindingResult();
} else {
result = ((BindException) e).getBindingResult();
}
FieldError error = result.getFieldError();
assert error != null;
message = error.getDefaultMessage();
} else {
if (e instanceof HttpMessageNotReadableException) {
message = "参数解析失败";
} else if (e instanceof ConstraintViolationException) {
ConstraintViolationException ex = (ConstraintViolationException) e;
Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
ConstraintViolation<?> violation = violations.iterator().next();
message = violation.getMessage();
} else if (e instanceof ValidationException) {
message = "参数验证失败: " + e.getMessage();
} else {
message = e.getMessage();
}
}
return ResultEntity.fail(BasicErrorCode.PARAM_ERROR.getCode(), message);
}
/**
* 业务异常处理方法
*/
@ExceptionHandler(value = BizException.class)
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> bizException(BizException e) {
log.error(StrUtil.format("业务异常:{}", getApiAndTenantInfo()), e);
return ResultEntity.fail(e.getCode(), e.getMessage());
}
/**
* 系统异常处理方法
*/
@ExceptionHandler(value = SysException.class)
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> sysException(SysException e) {
log.error(StrUtil.format("系统异常:{}", getApiAndTenantInfo()), e);
return ResultEntity.fail(e.getCode(), e.getMessage());
}
/**
* 405 - Method Not Allowed
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
public ResultEntity<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
return ResultEntity.fail(BasicErrorCode.SERVER_ERROR.getCode(), "当前接口不支持【" + e.getMethod() + "】方法!");
}
/**
* 通用未知异常
*/
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> error(Exception e) {
log.error(StrUtil.format("系统错误:{}", getApiAndTenantInfo()), e);
if (Objects.nonNull(e.getCause()) && e.getCause().getCause() instanceof BizException) {
BizException bizException = (BizException) e.getCause().getCause();
return bizException(bizException);
}
if (Objects.nonNull(e.getCause()) && e.getCause().getCause() instanceof SysException) {
SysException sysException = (SysException) e.getCause().getCause();
return sysException(sysException);
}
if(Objects.nonNull(e) && Objects.nonNull(e.getMessage())){
if(ValidatorUtils.isContainChinese(e.getMessage())){
return ResultEntity.fail(BasicErrorCode.UNKNOWN_ERROR, e.getMessage());
}
}
return ResultEntity.fail(BasicErrorCode.SERVER_ERROR.getCode(), "系统错误,请联系管理员!");
}
/**
* 文件过大 RequestTooBigException.class
*/
@ExceptionHandler({MultipartException.class})
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> requestTooBigException(MultipartException e) {
log.error(StrUtil.format("文件上传错误:{}", getApiAndTenantInfo()), e);
return ResultEntity.fail(BasicErrorCode.SERVER_ERROR.getCode(), "文件过大,无法上传");
}
/**
* 运行时业务异常处理方法
*/
@ExceptionHandler(value = {RuntimeException.class})
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> runtimeException(RuntimeException e) {
log.error(StrUtil.format("业务异常:{}", getApiAndTenantInfo()), e);
if(e instanceof BaseException || (Objects.nonNull(e.getCause()) && e.getCause() instanceof BaseException) || (Objects.nonNull(e.getCause()) && Objects.nonNull(e.getCause().getCause()) && e.getCause().getCause() instanceof BaseException)){
BaseException baseException = null;
if(e instanceof BaseException){
baseException = (BaseException) e;
}else if(Objects.nonNull(e.getCause()) && e.getCause() instanceof BaseException) {
baseException = (BaseException) e.getCause();
}else{
baseException = (BaseException) e.getCause().getCause();
}
ErrorCode errorCode = Objects.nonNull(baseException.getErrorCode()) ? baseException.getErrorCode() : BasicErrorCode.UNKNOWN_ERROR;
String errorMsg = StringUtils.hasLength(baseException.getMessage()) ? baseException.getMessage() : errorCode.getMessage();
return ResultEntity.fail(errorCode, errorMsg);
}
if(Objects.nonNull(e) && Objects.nonNull(e.getMessage())){
if(ValidatorUtils.isContainChinese(e.getMessage())){
return ResultEntity.fail(BasicErrorCode.UNKNOWN_ERROR, e.getMessage());
}
}
return ResultEntity.fail(BasicErrorCode.UNKNOWN_ERROR, BasicErrorCode.UNKNOWN_ERROR.getMessage());
}
/**
* 通用业务异常处理方法
*/
@ExceptionHandler(value = {Exception.class})
@ResponseStatus(HttpStatus.OK)
public ResultEntity<?> exception(Exception e) {
log.error(StrUtil.format("业务异常:{}", getApiAndTenantInfo()), e);
if(Objects.nonNull(e.getCause()) && Objects.nonNull(e.getCause().getCause()) && e.getCause().getCause() instanceof BaseException){
BaseException baseException = (BaseException) e.getCause().getCause();
ErrorCode errorCode = Objects.nonNull(baseException.getErrorCode()) ? baseException.getErrorCode() : BasicErrorCode.UNKNOWN_ERROR;
String errorMsg = StringUtils.hasLength(baseException.getMessage()) ? baseException.getMessage() : BasicErrorCode.UNKNOWN_ERROR.getMessage();
return ResultEntity.fail(errorCode, errorMsg);
}
if(Objects.nonNull(e) && Objects.nonNull(e.getMessage())){
if(ValidatorUtils.isContainChinese(e.getMessage())){
return ResultEntity.fail(BasicErrorCode.UNKNOWN_ERROR, e.getMessage());
}
}
return ResultEntity.fail(BasicErrorCode.UNKNOWN_ERROR, BasicErrorCode.UNKNOWN_ERROR.getMessage());
}
private String getApiAndTenantInfo() {
String api = null;
HttpServletRequest request = ServletUtils.getRequest();
if (request != null) {
api = request.getRequestURI();
}
return StrUtil.format(" 接口:{};", api);
}
}
package com.gitlab.exception;
import com.gitlab.enums.BasicErrorCode;
/**
* @Description SysException 系统异常定义
* @Author huangshun
* @Date 2023/6/18
*/
public class SysException extends BaseException {
private static final long serialVersionUID = 4355163994767354840L;
public SysException(String message) {
super(message);
this.setErrorCode(BasicErrorCode.SERVER_ERROR);
}
public SysException(ErrorCode errorCode, String message) {
super(message);
this.setErrorCode(errorCode);
}
public SysException(String message, Throwable e) {
super(message,e);
this.setErrorCode(BasicErrorCode.SERVER_ERROR);
}
}
package com.gitlab.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Description AccessTokenDTO 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Data
@Accessors(chain = true)
public class AccessTokenDTO {
/**
* access_token
*/
private String access_token;
/**
* refresh_token
*/
private String refresh_token;
/**
* scope
*/
private String scope;
/**
* id_token
*/
private String id_token;
/**
* token_type
*/
private String token_type;
/**
* expires_in
*/
private Long expires_in;
}
package com.gitlab.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Description AuthorizeRequestDTO 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Data
@Accessors(chain = true)
public class AuthorizeRequestDTO {
/**
* 类型
*/
private String response_type;
/**
* client_id
*/
private String client_id;
/**
* redirect_uri
*/
private String redirect_uri;
/**
* scope
*/
private String scope;
/**
* state
*/
private String state;
}
package com.gitlab.model;
import com.gitlab.enums.ResultEnum;
import com.gitlab.exception.ErrorCode;
import com.gitlab.enums.BasicErrorCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.http.HttpStatus;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* @Description ResultEntity 类(或接口)
* @Author huangshun
* @Date 2023/6/17
*/
@Data
@Schema(description = "统一返回对象")
public class ResultEntity<T> {
private static final long serialVersionUID = 1L;
/**
* 响应编号
*/
@Schema(description = "响应编号")
private String code;
/**
* 响应信息
*/
@Schema(description = "响应信息")
private String msg;
/**
* 响应数据
*/
@Schema(description = "响应数据")
private T data;
/**
* 时间戳
*/
@Schema(description = "时间戳")
private Long ts = 0L;
/**
* 响应状态
*/
@Schema(description = "响应状态")
private boolean success = false;
public boolean getSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public static <T> ResultEntity<T> ok() {
ResultEntity<T> r = restResult(null, ResultEnum.SUCCESS.getCode(), null);
r.setSuccess(true);
return r;
}
public static <T> ResultEntity<T> ok(T data) {
ResultEntity<T> r = restResult(data, ResultEnum.SUCCESS.getCode(), null);
r.setSuccess(true);
return r;
}
public static <T> ResultEntity<T> ok(T data, String msg) {
ResultEntity<T> r = restResult(data, ResultEnum.SUCCESS.getCode(), msg);
r.setSuccess(true);
return r;
}
public static <T> ResultEntity<T> fail() {
return restResult(null, BasicErrorCode.SERVER_ERROR.getCode(), null);
}
public static <T> ResultEntity<T> fail(String msg) {
return restResult(null, BasicErrorCode.SERVER_ERROR.getCode(), msg);
}
public static <T> ResultEntity<T> fail(T data) {
return restResult(data, BasicErrorCode.SERVER_ERROR.getCode(), null);
}
public static <T> ResultEntity<T> fail(ErrorCode errorCode) {
return restResult(null, errorCode.getCode(), errorCode.getMessage());
}
public static <T> ResultEntity<T> fail(ErrorCode errorCode, String msg) {
return restResult(null, errorCode.getCode(), msg);
}
public static <T> ResultEntity<T> fail(ErrorCode errorCode, T data) {
return restResult(data, errorCode.getCode(), errorCode.getMessage());
}
public static <T> ResultEntity<T> fail(HttpStatus httpStatus, String msg) {
return restResult(null, httpStatus.value() + "", msg);
}
public static <T> ResultEntity<T> fail(String msg, T data) {
return restResult(data, BasicErrorCode.SERVER_ERROR.getCode(), msg);
}
public static <T> ResultEntity<T> fail(String code, String msg) {
return restResult(null, code, msg);
}
private static <T> ResultEntity<T> restResult(T data, String code, String msg) {
ResultEntity<T> apiResult = new ResultEntity<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
apiResult.setTs(System.currentTimeMillis());
return apiResult;
}
public static <T> T data(ResultEntity<T> resultEntity, T defaultIfNull) {
return Optional.ofNullable(resultEntity)
.map(ResultEntity::getData)
.orElse(defaultIfNull);
}
public static <T> Optional<T> data(ResultEntity<T> resultEntity) {
return Optional.ofNullable(resultEntity)
.map(ResultEntity::getData);
}
public static <T> T dataThrow(ResultEntity<T> resultEntity, RuntimeException e) {
return Optional.ofNullable(resultEntity)
.map(ResultEntity::getData)
.orElseThrow(() -> e);
}
public static <T> void successThrow(ResultEntity<T> resultEntity, Function<Optional<String>, RuntimeException> exceptionFunction) {
boolean success = Optional.ofNullable(resultEntity).map(ResultEntity::getSuccess).orElse(false);
if (!success) {
throw exceptionFunction.apply(Optional.ofNullable(resultEntity).map(ResultEntity::getMsg));
}
}
/**
* 如果失败就消费
*
* @param resultEntity resultEntity
* @param failConsumer 失败如何消费
* @param <T> T
*/
public static <T> void failConsumer(ResultEntity<T> resultEntity, Consumer<Optional<String>> failConsumer) {
boolean success = Optional.ofNullable(resultEntity).map(ResultEntity::getSuccess).orElse(false);
if (!success) {
if (failConsumer != null) {
failConsumer.accept(Optional.ofNullable(resultEntity).map(ResultEntity::getMsg));
}
}
}
public static <T> boolean isSuccess(ResultEntity<T> resultEntity) {
return Optional.ofNullable(resultEntity).map(ResultEntity::getSuccess).orElse(false);
}
}
package com.gitlab.model;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @Description TokenRequestDTO 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Data
@Accessors(chain = true)
public class TokenRequestDTO {
/**
* grant_type
*/
private String grant_type;
/**
* client_id
*/
private String client_id;
/**
* client_secret
*/
private String client_secret;
/**
* redirect_uri
*/
private String redirect_uri;
/**
* code
*/
private String code;
}
package com.gitlab.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @Description UserinfoDTO 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
//@Data
@Accessors(chain = true)
public class UserinfoDTO {
/**
* 用户ID
*/
private String userId;
/**
* 所属组织
*/
private String orgName;
/**
* 真实姓名
*/
private String realname;
/**
* 姓名拼音
*/
private String enName;
/**
* 身份证号
*/
private String cardNum;
/**
* 电子邮箱
*/
private String email;
/**
* 用户名
*/
private String username;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getOrgName() {
return orgName;
}
public void setOrgName(String orgName) {
this.orgName = orgName;
}
public String getRealname() {
return realname;
}
public void setRealname(String realname) {
this.realname = realname;
}
public String getEnName() {
return enName;
}
public void setEnName(String enName) {
this.enName = enName;
}
public String getCardNum() {
return cardNum;
}
public void setCardNum(String cardNum) {
this.cardNum = cardNum;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
package com.gitlab.util;
public class Base64Utils
{
private static char[] Base64Code = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', '+', '/' };
private static byte[] Base64Decode = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, 63, -1, 63, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
-1, 0, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, -1, -1, -1, -1, -1, -1, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1 };
public static String encode(byte[] b)
{
int code = 0;
if (b == null)
return null;
StringBuffer sb = new StringBuffer((b.length - 1) / 3 << 6);
for (int i = 0; i < b.length; i++)
{
code |= b[i] << 16 - i % 3 * 8 & 255 << 16 - i % 3 * 8;
if ((i % 3 != 2) && (i != b.length - 1))
continue;
sb.append(Base64Code[((code & 0xFC0000) >>> 18)]);
sb.append(Base64Code[((code & 0x3F000) >>> 12)]);
sb.append(Base64Code[((code & 0xFC0) >>> 6)]);
sb.append(Base64Code[(code & 0x3F)]);
code = 0;
}
if (b.length % 3 > 0)
sb.setCharAt(sb.length() - 1, '=');
if (b.length % 3 == 1)
sb.setCharAt(sb.length() - 2, '=');
return sb.toString();
}
public static byte[] decode(String code)
{
if (code == null)
return null;
int len = code.length();
if (len % 4 != 0)
throw new IllegalArgumentException("Base64 string length must be 4*n");
if (code.length() == 0)
return new byte[0];
int pad = 0;
if (code.charAt(len - 1) == '=')
pad++;
if (code.charAt(len - 2) == '=')
pad++;
int retLen = len / 4 * 3 - pad;
byte[] ret = new byte[retLen];
for (int i = 0; i < len; i += 4)
{
int j = i / 4 * 3;
char ch1 = code.charAt(i);
char ch2 = code.charAt(i + 1);
char ch3 = code.charAt(i + 2);
char ch4 = code.charAt(i + 3);
int tmp = Base64Decode[ch1] << 18 | Base64Decode[ch2] << 12 | Base64Decode[ch3] << 6 | Base64Decode[ch4];
ret[j] = (byte)((tmp & 0xFF0000) >> 16);
if (i < len - 4)
{
ret[(j + 1)] = (byte)((tmp & 0xFF00) >> 8);
ret[(j + 2)] = (byte)(tmp & 0xFF);
}
else {
if (j + 1 < retLen)
ret[(j + 1)] = (byte)((tmp & 0xFF00) >> 8);
if (j + 2 < retLen)
ret[(j + 2)] = (byte)(tmp & 0xFF);
}
}
return ret;
}
}
\ No newline at end of file
package com.gitlab.util;
import cn.hutool.jwt.JWT;
import java.nio.charset.StandardCharsets;
import java.util.function.Function;
/**
* @Description JWTUtils 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
public class JWTUtils {
/**
* 生成token
* @param sk
* @param sub
* @param data
* @param expiredAt
* @return
*/
public static String create(String sk,String sub, String data,Long expiredAt) {
try {
byte[] key = sk.getBytes(StandardCharsets.UTF_8);
return JWT.create()
.setKey(key)
.setPayload("sub",sub)
.setPayload("data",data)
.setPayload("exp", expiredAt)
.sign();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 验证token
* @param token
* @param sk
* @return
*/
public static Boolean verify(String token,String sk) {
return JWT.of(token).setKey(sk.getBytes(StandardCharsets.UTF_8)).verify();
}
/**
* 获取数据
* @param token
* @param getPayload
* @param <T>
* @return
*/
public static <T> T of(String token, Function<JWT,T> getPayload) {
JWT jwt = JWT.of(token);
return getPayload.apply(jwt);
}
}
package com.gitlab.util;
import java.security.MessageDigest;
public class MD5Utils {
/**
* MD5生成
* @param s 报文串
* @param charset 编码格式
* @return
*/
public static String MD5Encoder(String s, String charset) {
char hexDigits[] = { '0', '1', '2', '3', '4','5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F' };
try {
byte[] btInput = s.getBytes(charset);
//获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
//使用指定的字节更新摘要
mdInst.update(btInput);
//获得密文
byte[] md = mdInst.digest();
//把密文转换成十六进制的字符串形式
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
package com.gitlab.util;
import com.gitlab.config.OAuth2Properties;
import lombok.extern.slf4j.Slf4j;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Map;
/**
* @Description OAuth2SecurityUtils 类(或接口)
* @Author huangshun
* @Date 2024/4/22
*/
@Slf4j
public class OAuth2SecurityUtils {
private OAuth2Properties properties;
public OAuth2SecurityUtils(OAuth2Properties properties) {
this.properties = properties;
}
/**
* 公钥加密
*
* @param str
* @return
* @throws Exception
*/
public String encryptByPublicKey(String str) throws Exception {
String md5 = MD5Utils.MD5Encoder(this.properties.getKey() + str, "UTF-8");
byte[] data = (str + md5).getBytes();
//公钥加密
byte[] encodedData = RSAUtils.encryptByPublicKey(data, this.properties.getSinglePublicKey());
String yonghuKey = URLEncoder.encode(Base64Utils.encode(encodedData), "UTF-8");
return yonghuKey;
}
/**
* 私钥解密
*
* @param ecode
* @return
* @throws Exception
*/
public String decryptByPrivateKey(String ecode) throws Exception {
// 第一步:先进行URL解码
String urlDecoderData = URLDecoder.decode(ecode, "UTF-8");
// 第二步:进行Base64解码
byte[] encryptedData = Base64Utils.decode(urlDecoderData);
// 第三步:私钥解密
byte[] decoderData = RSAUtils.decryptByPrivateKey(encryptedData, this.properties.getSinglePrivateKey());
// 第四步:将解密的字节数组转换成字符串
String data = new String(decoderData, "UTF-8");
// 第五步:减去MD5的固定长度32,得到原始数据
String str = data.substring(0, data.length() - 32);
return str;
}
/**
* 生成公钥和私钥的
*/
private void genKeyPair() throws Exception {
Map<String, Object> keymap = RSAUtils.genKeyPair();
// com.ufgov.singlePublicKey
String singlePublicKey = RSAUtils.getPublicKey(keymap);
// com.ufgov.singlePrivateKey
String singlePrivateKey = RSAUtils.getPrivateKey(keymap);
System.out.println("oauth2.key=" + this.properties.getKey());
System.out.println("oauth2.single-public-key=" + singlePublicKey);
System.out.println("oauth2.single-private-key=" + singlePrivateKey);
}
}
package com.gitlab.util;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Description ServletUtils 类(或接口)
* @Author huangshun
* @Date 2023/7/18
*/
@Slf4j
public class ServletUtils {
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
try {
ServletRequestAttributes requestAttributes = getRequestAttributes();
return requestAttributes == null ? null : requestAttributes.getRequest();
} catch (Exception e) {
return null;
}
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
try {
ServletRequestAttributes requestAttributes = getRequestAttributes();
return requestAttributes == null ? null : requestAttributes.getResponse();
} catch (Exception e) {
return null;
}
}
public static ServletRequestAttributes getRequestAttributes() {
try {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
} catch (Exception e) {
return null;
}
}
public static Map<String, String> getHeaders(HttpServletRequest request) {
if (request == null) {
return null;
}
Map<String, String> map = new LinkedHashMap<>(16);
Enumeration<String> enumeration = request.getHeaderNames();
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
}
return map;
}
public static Map<String, String> getHeaders() {
return getHeaders(getRequest());
}
public static String getHeader(String key) {
HttpServletRequest request = getRequest();
if (request == null) {
return null;
}
return request.getHeader(key);
}
/**
* 是否是Ajax异步请求
*
* @param request req
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
String accept = request.getHeader("accept");
if (accept != null && accept.contains("application/json")) {
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
return true;
}
String uri = request.getRequestURI();
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
return true;
}
String ajax = request.getParameter("__ajax");
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
}
/**
* 对下载的文件名称进行转码
*
* @param filename filename
* @param request request
* @param response response
* @return return
* @throws UnsupportedEncodingException e
*/
public static String encodeFilename(String filename, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
String userAgent = request.getHeader("User-Agent");
response.setCharacterEncoding("UTF-8");
String encodeFilename;
if (/* IE 8 至 IE 10 */
userAgent.toUpperCase().contains("MSIE") ||
/* IE 11 */
userAgent.contains("Trident/7.0")) {
encodeFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
} else if (userAgent.toUpperCase().contains("MOZILLA") ||
userAgent.toUpperCase().contains("CHROME")) {
encodeFilename = new String(filename.getBytes(), StandardCharsets.ISO_8859_1);
} else {
encodeFilename = URLEncoder.encode(filename, StandardCharsets.UTF_8.toString());
}
return encodeFilename;
}
public static void exportTemplate(String excelTemplate, String templateName, HttpServletRequest request, HttpServletResponse response) {
response.setContentType("application/vnd.ms-excel");
try {
response.setHeader("Content-Disposition", "attachment;filename=" + ServletUtils.encodeFilename(templateName, request, response) + ".xlsx");
ClassPathResource resource = new ClassPathResource(excelTemplate);
ServletOutputStream out = response.getOutputStream();
InputStream is = resource.getInputStream();
byte[] bis = new byte[1024];
int n;
while ((n = is.read(bis)) != -1) {
out.write(bis, 0, n);
}
out.flush();
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取body
*
* @param request 请求
* @return body
*/
public static String getBodyString(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
try (InputStream inputStream = request.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("body读取失败", e);
}
return sb.toString();
}
}
package com.gitlab.util;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
/**
* @author: ShiYunXiao
* @date: 2023/05/24 09:48
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* 下划线
*/
public static final String UNDERLINE = "_";
/**
* 下划线转驼峰命名
*/
public static String toUnderScoreCase(String str) {
if (str == null) {
return null;
}
StringBuilder sb = new StringBuilder();
// 前置字符是否大写
boolean preCharIsUpperCase;
// 当前字符是否大写
boolean curreCharIsUpperCase;
// 下一字符是否大写
boolean nexteCharIsUpperCase = true;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (i > 0) {
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
} else {
preCharIsUpperCase = false;
}
curreCharIsUpperCase = Character.isUpperCase(c);
if (i < (str.length() - 1)) {
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
}
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
sb.append(UNDERLINE);
} else if (i != 0 && !preCharIsUpperCase && curreCharIsUpperCase) {
sb.append(UNDERLINE);
}
sb.append(Character.toLowerCase(c));
}
return sb.toString();
}
/**
* 是否包含字符串
*
* @param str 验证字符串
* @param strList 字符串组
* @return 包含返回true
*/
public static boolean inStringIgnoreCase(String str, String... strList) {
if (str != null && strList != null) {
for (String s : strList) {
if (str.equalsIgnoreCase(trim(s))) {
return true;
}
}
}
return false;
}
/**
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
*
* @param name 转换前的下划线大写方式命名的字符串
* @return 转换后的驼峰式命名的字符串
*/
public static String convertToCamelCase(String name) {
StringBuilder result = new StringBuilder();
// 快速检查
if (name == null || name.isEmpty()) {
// 没必要转换
return "";
} else if (!name.contains(UNDERLINE)) {
// 不含下划线,仅将首字母大写
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
// 用下划线将原始字符串分割
String[] camels = name.split(UNDERLINE);
for (String camel : camels) {
// 跳过原始字符串中开头、结尾的下换线或双重下划线
if (camel.isEmpty()) {
continue;
}
// 首字母大写
result.append(camel.substring(0, 1).toUpperCase());
result.append(camel.substring(1).toLowerCase());
}
return result.toString();
}
/**
* 驼峰式命名法 例如:user_name->userName
*/
public static String toCamelCase(String s) {
if (s == null) {
return null;
}
s = s.toLowerCase();
StringBuilder sb = new StringBuilder(s.length());
boolean upperCase = false;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == UNDERLINE.charAt(0)) {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(c));
upperCase = false;
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* 从uri的query中获取所有参数
*
* @param queryStr uriQuery,如:userId=1&username=test
* @return uriQuery中的参数, 如key:userId value:1
*/
public static Map<String, String> getUriQueryMap(String queryStr) {
Map<String, String> queryMap = new HashMap<>(16);
if (!org.apache.commons.lang3.StringUtils.isEmpty(queryStr)) {
String[] queryParam = queryStr.split("&");
Arrays.stream(queryParam).forEach(s -> {
String[] kv = s.split("=", 2);
String value = kv.length == 2 ? kv[1] : "";
queryMap.put(kv[0], value);
});
}
return queryMap;
}
/**
* 多个字符串通过分隔符分割组成字符串
*
* @param delimiter 分隔符
* @param objects 字符串对象
* @return 字符串
*/
public static String join(CharSequence delimiter, Object... objects) {
StringJoiner sj = new StringJoiner(delimiter);
for (Object object : objects) {
if (object != null) {
sj.add(object.toString());
}
}
return sj.toString();
}
/**
* 左侧补0
* totalLength为补充0后的总长度,必须>0,否则返回obj的原本内容
* obj不能为空
* example1: totalLength = [0,-1,-2] | obj = 123 | result = 123
* example2: totalLength = [1,2,3] | obj = 123 | result = 123
* example3: totalLength = 4 | obj = 123 | result = 0123
*
* @param totalLength 总长度
* @param obj 需要补充0的对象
* @return 左补0的字符串
*/
public static String addZeroLeft(int totalLength, Object obj) {
if (obj == null) {
return null;
}
if (totalLength <= 0) {
return obj.toString();
}
return String.format("%0" + totalLength + "d", obj);
}
/**
* 按最大长度截取字符串
*
* @param str 字符串
* @param maxLength 最大长度
* @return 新字符串
*/
public static String substringByMaxLength(String str, int maxLength) {
if (str != null && str.length() > maxLength) {
return str.substring(0, maxLength);
}
return str;
}
/**
* Trim the trailing linebreak (if any) from this string.
*
* @param str 目标字符串
* @return 替换后的字符串
*/
public static String trimLineBreak(String str) {
if (!hasLength(str)) {
return str;
}
StringBuilder buf = new StringBuilder(str);
while (buf.length() > 0 && isLineBreakCharacter(buf.charAt(buf.length() - 1))) {
buf.deleteCharAt(buf.length() - 1);
}
return buf.toString();
}
private static boolean isLineBreakCharacter(char ch) {
return '\n' == ch || '\r' == ch;
}
public static boolean hasLength(String str) {
return str != null && str.length() > 0;
}
}
package com.gitlab.util;
import cn.hutool.core.lang.Validator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description Validator hutool字符验证扩展
* @Author huangshun
* @Date 2024/1/9
*/
public class ValidatorUtils extends Validator {
/**
* 验证是否是汉字 或 者0-9、a-z、A-Z
*
* @param c 被验证的char
* @return true代表符合条件
*/
public static boolean isRightChar(char c) {
return isChinese(c) || isWord(c);
}
/**
* 校验某个字符是否是a-z、A-Z、_、0-9
*
* @param c 被校验的字符
* @return true 代表符合条件
*/
public static boolean isWord(char c) {
String regEx = "[\\w]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher("" + c);
return m.matches();
}
/**
* 判定输入的是否是汉字
*
* @param c 被校验的字符
* @return true 代表是汉字
*/
public static boolean isChinese(char c) {
Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
return true;
}
return false;
}
/**
* 判断字符串中是否包含中文
*
* @param str 待校验字符串
* @return 是否为中文
* @warn 不能校验是否为中文标点符号
*/
public static boolean isContainChinese(String str) {
Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
Matcher m = p.matcher(str);
if (m.find()) {
return true;
}
return false;
}
/**
* 过滤掉中文
*
* @param str 待过滤中文的字符串
* @return 过滤掉中文后字符串
*/
public static String filterChinese(String str) {
// 用于返回结果
String result = str;
boolean flag = isContainChinese(str);
if (flag) {// 包含中文
// 用于拼接过滤中文后的字符
StringBuffer sb = new StringBuffer();
// 用于校验是否为中文
boolean flag2 = false;
// 用于临时存储单字符
char chinese = 0;
// 5.去除掉文件名中的中文
// 将字符串转换成char[]
char[] charArray = str.toCharArray();
// 过滤到中文及中文字符
for (int i = 0; i < charArray.length; i++) {
chinese = charArray[i];
flag2 = isChinese(chinese);
if (!flag2) {// 不是中日韩文字及标点符号
sb.append(chinese);
}
}
result = sb.toString();
}
return result;
}
/**
* 过滤掉 中文 以及 a-z、A-Z、_、0-9
*
* @param str 待过滤中文的字符串
* @return 过滤掉中文后字符串
*/
public static String filterChineseAndWord(String str) {
// 用于返回结果
String result = str;
boolean flag = isContainChinese(str);
if (flag) {// 包含中文
// 用于拼接过滤中文后的字符
StringBuffer sb = new StringBuffer();
// 用于校验是否为中文
boolean flag2 = false;
// 用于校验是否为字母以及数字
boolean flag3 = false;
// 用于临时存储单字符
char chinese = 0;
// 5.去除掉文件名中的中文
// 将字符串转换成char[]
char[] charArray = str.toCharArray();
// 过滤到中文及中文字符
for (int i = 0; i < charArray.length; i++) {
chinese = charArray[i];
flag2 = isChinese(chinese);
flag3 = isWord(chinese);
if (!flag2 && !flag3) {// 不是中日韩文字及标点符号
sb.append(chinese);
}
}
result = sb.toString();
}
return result;
}
/**
* 校验一个字符是否是汉字
*
* @param c 被校验的字符
* @return true代表是汉字
*/
public static boolean isChineseChar(char c) {
try {
return String.valueOf(c).getBytes("UTF-8").length > 1;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 验证字符串内容是否包含下列非法字符<br>
* `~!#%^&*=+\\|{};:'\",<>/?○●★☆☉♀♂※¤╬の〆
*
* @param content 字符串内容
* @return 't'代表不包含非法字符,otherwise代表包含非法字符。
*/
public static char validateLegalString(String content) {
String illegal = "`~!#%^&*=+\\|{};:'\",<>/?○●★☆☉♀♂※¤╬の〆";
char isLegalChar = 't';
L1:
for (int i = 0; i < content.length(); i++) {
for (int j = 0; j < illegal.length(); j++) {
if (content.charAt(i) == illegal.charAt(j)) {
isLegalChar = content.charAt(i);
break L1;
}
}
}
return isLegalChar;
}
/**
* 校验String是否全是中文
*
* @param name 被校验的字符串
* @return true代表全是汉字
*/
public static boolean checkNameisWord(String name) {
boolean res = true;
char[] cTemp = name.toCharArray();
for (int i = 0; i < name.length(); i++) {
if (!isWord(cTemp[i])) {
res = false;
break;
}
}
return res;
}
/**
* 判断字符串是否全为英文
*
* @param str
* @return
*/
public void judge(String str) {
//【全为英文】返回true 否则false
boolean result1 = str.matches("[a-zA-Z]+");
//【全为数字】返回true
Boolean result6 = str.matches("[0-9]+");
//【除英文和数字外无其他字符(只有英文数字的字符串)】返回true 否则false
boolean result2 = str.matches("[a-zA-Z0-9]+");
//【含有英文】true
String regex1 = ".*[a-zA-z].*";
boolean result3 = str.matches(regex1);
//【含有数字】true
String regex2 = ".*[0-9].*";
boolean result4 = str.matches(regex2);
//判断是否为纯中文,不是返回false
String regex3 = "[\\u4e00-\\u9fa5]+";
boolean result5 = str.matches(regex3);
System.out.println(result1 + "--" + result2 + "--" + result3
+ "--" + result4 + "--" + result5 + "--" + result6);
}
/**
* 判断字符串是否为全中文
* 利用正则表达式来判 1 不是 2 是
*/
public static int checkIsChinese(String str) {
String regex3 = "[\\u4e00-\\u9fa5]+";
if (!str.matches(regex3)) {
return 1;
}
return 2;
}
public static int isThereNumber(String str) {
String regex2 = ".*[0-9].*";
if (!str.matches(regex2)) {
return 3;
}
return 4;
}
}
################### 项目启动端口 ###################
server:
port: 8180
servlet:
context-path: /auth
################### oauth2配置 ###################
oauth2:
client_id: "1776908293357174784"
client_secret: "05f0e1781b814ed292c3a817cd9c6e29"
redirect_uri: "http://192.168.1.231:8200/hub/oauth_callback"
code-expires: 120
token-expires: 7200
key: oauth2
single-private-key: "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANzxscBOcjia1OlJntu0U0UaTwEM275kIj/4I55BIiWz5wo/0JxycZdOY5eXfOSNyYbw5hDV7FeqCnYErZpXRGZgG23wOWTiiqW6Vg9blejJrmD2co9BcsIvik7IoXxOVSfmfc1fpGWRJaw5xbWHwofxcJQ9FlEyu6AaCRIqM47JAgMBAAECgYAJhRvYwpM3BNYzXm81s0LlQYQcR6QOQ5Ka5AO7ir3rFvctC2E0jyp9eldd4vKdhyMi/oKYlhxyuIpuhGFrOuMLsQm+MpzYFIyNVEEy71Hq+h3fie2ai5b/AD2wCx0OXbPZCDCha/Tj/Nr2Jxud5xAl48ZTtJYEvqYLSjxL8PB4KwJBAPed0pBc7MVtDXDZNeTGSD7oxp7D5gJUYmp1Ij4XnRKoX80nuschfUzBic3HxxSJ/RKjtMYM+PdPPUsrDffzbhsCQQDkbLFyiGHtdqGJVnwCngqEUZAlgYBT4BWmxWZ9LDmrjm+GFF3RZoWI21j51JdIQpB3w6CxZjUxdIU7e0KIIDTrAkEA8tsairZpDpUPiq+u+Qs0DmdVbp+p9nz27XymsgmM56C2HUurF+UTtHVZh7c53T4dNOvUwC42/K/96Lx4fciGIwJBAJmHmOkjU7a4wrA9idJ0iRsQezNKTIeTmNnj2hQN8qEldj4HWFuTbfNjgvVAd4IhA1sMCOjTirM33wjwuvIlu4sCQQDdeVZXL+CFFv2Z8u5M+MMJrXj8Jyiva5fYrMz1OIFcawqWYh1d7mclY9HAHXBnKWOY8T2eZus0ARtvEjNzyOgh"
single-public-key: "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDc8bHATnI4mtTpSZ7btFNFGk8BDNu+ZCI/+COeQSIls+cKP9CccnGXTmOXl3zkjcmG8OYQ1exXqgp2BK2aV0RmYBtt8Dlk4oqlulYPW5Xoya5g9nKPQXLCL4pOyKF8TlUn5n3NX6RlkSWsOcW1h8KH8XCUPRZRMrugGgkSKjOOyQIDAQAB"
redirect_login: "http://scjoyedu.eicp.net:58000/auth/login"
################### spring配置 ###################
knife4j:
enable: true
production: false
logging:
level:
root: info
springdoc:
# knife4j Get DTO 解析 默认是false,需要设置为true
default-flat-param-object: true
################### spring配置 ###################
spring:
servlet:
multipart:
max-request-size: 100MB
max-file-size: 100MB
# 环境 dev|test|prod
profiles:
active: dev
application:
name: gitlab-sso-oauth2
# springboot 高版本默认关闭三级缓存解决循环依赖 这里需要配置三级缓存为开启状态
main:
allow-circular-references: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Chongqing
server:
servlet:
context-path: /auth
spring:
application:
name: gitlab-sso-oauth2
profiles:
active: dev
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitlab</groupId>
<artifactId>gitlab-sso</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>gitlab-sso-oauth2</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>3.1.0</spring.boot.version>
<hutool.version>5.8.11</hutool.version>
<jasypt.version>1.9.2</jasypt.version>
<fastjson.version>2.0.25</fastjson.version>
<okhttp3.version>4.10.0</okhttp3.version>
<retrofit2.version>2.9.0</retrofit2.version>
<jtokkit.version>0.5.0</jtokkit.version>
<josejwt.version>9.31</josejwt.version>
<commons-codec.version>1.11</commons-codec.version>
<oltu-oauth2.version>0.31</oltu-oauth2.version>
<knife4j.version>4.0.0</knife4j.version>
<knife4j-swagger-models-v3.version>2.2.7</knife4j-swagger-models-v3.version>
</properties>
<!-- 统一管理项目中依赖包的版本号 -->
<dependencyManagement>
<dependencies>
<!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${springboot.version}</version>
<scope>test</scope>
</dependency>
<!-- 工具类 -->
<!--hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>${josejwt.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
</dependency>
<!-- OKHTTP -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>logging-interceptor</artifactId>
<version>${okhttp3.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.knuddels</groupId>
<artifactId>jtokkit</artifactId>
<version>${jtokkit.version}</version>
</dependency>
<!-- 加密解密工具 -->
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>${jasypt.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- knife4j api docs -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>${knife4j-swagger-models-v3.version}</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-models-jakarta</artifactId>
<version>${knife4j-swagger-models-v3.version}</version>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
<version>${oltu-oauth2.version}</version>
</dependency>
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
<version>${oltu-oauth2.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论