瀏覽代碼

Merge branch 'release' of gitadmin/tuoheng_oidc into master

tags/v2.5.2
xuziqing 1 年之前
父節點
當前提交
4fd995b428
共有 19 個文件被更改,包括 977 次插入4 次删除
  1. +4
    -3
      tuoheng_oidc_admin/src/main/java/com/tuoheng/service/impl/TenantServiceImpl.java
  2. +132
    -0
      tuoheng_oidc_admin/src/main/java/com/tuoheng/until/JacksonUtil.java
  3. +4
    -0
      tuoheng_oidc_server/pom.xml
  4. +1
    -1
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/MyCorsFilter.java
  5. +1
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java
  6. +32
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/HeadClientHttpRequestInterceptor.java
  7. +64
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/RestProperties.java
  8. +175
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/RestTemplateConfig.java
  9. +16
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/constants/OidcConstant.java
  10. +14
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/constants/ThirdConstant.java
  11. +40
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/controller/ThirdController.java
  12. +17
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/RegisteredClientMapper.java
  13. +19
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/RegisteredClientPo.java
  14. +22
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/result/ProfileResult.java
  15. +26
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/result/TokenResult.java
  16. +10
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/ThirdService.java
  17. +147
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/ThirdServiceImpl.java
  18. +242
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/HttpUtils.java
  19. +11
    -0
      tuoheng_oidc_server/src/main/resources/mapper/RegisteredClientMapper.xml

+ 4
- 3
tuoheng_oidc_admin/src/main/java/com/tuoheng/service/impl/TenantServiceImpl.java 查看文件

@@ -36,6 +36,7 @@ import com.tuoheng.model.vo.TenantVo;
import com.tuoheng.model.vo.UserVo;
import com.tuoheng.service.TenantService;
import com.tuoheng.third.service.impl.ThirdServiceImpl;
import com.tuoheng.until.JacksonUtil;
import com.tuoheng.until.JsonResult;
import com.tuoheng.until.MapUtils;
import lombok.extern.slf4j.Slf4j;
@@ -198,7 +199,7 @@ public class TenantServiceImpl implements TenantService {
createClientTenantDto.setClientRoleDtoList(list);
for (ClientRoleDto clientRoleDto : list) {
try {
log.info("参数:{}", clientRoleDto.toString());
log.info("参数:{}", JacksonUtil.obj2json(clientRoleDto));
JsonResult result = getResult(createClientTenantDto, clientRoleDto.getClientId(), DictConstant.ADD_TENANT, loginUser);
if (JsonResult.SUCCESS != result.getCode()) {
throw new ServiceException(HttpStatus.BAD_REQUEST.value(), result.getMsg());
@@ -702,7 +703,7 @@ public class TenantServiceImpl implements TenantService {
dto.setClientRoleDtoList(thirdService.getModelList(dto.getClientRoleDtoList()));
for (ClientRoleDto clientRoleDto : insertClientRoleDtoList) {
try {
log.info("参数:{}", clientRoleDto.toString());
log.info("参数:{}", JacksonUtil.obj2json(clientRoleDto));
JsonResult result = getResult(dto, clientRoleDto.getClientId(), DictConstant.ADD_TENANT, loginUser);
if (JsonResult.SUCCESS != result.getCode()) {
throw new ServiceException(HttpStatus.BAD_REQUEST.value(), result.getMsg());
@@ -771,7 +772,7 @@ public class TenantServiceImpl implements TenantService {
url = url + dictData.getValue();
HttpEntity httpEntity = new HttpEntity(dto, resultRequestHeader);
log.info("请求url:{}", url);
log.info("请求参数:{}", dto.toString());
log.info("请求参数:{}", JacksonUtil.obj2json(dto));
ResponseEntity<JsonResult> response;
try {
response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, JsonResult.class);

+ 132
- 0
tuoheng_oidc_admin/src/main/java/com/tuoheng/until/JacksonUtil.java 查看文件

@@ -0,0 +1,132 @@
package com.tuoheng.until;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Map;

public class JacksonUtil {

private final static Logger LOGGER = LoggerFactory.getLogger(JacksonUtil.class);

private final static ObjectMapper objectMapper = new ObjectMapper();

private final static SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

private JacksonUtil(){}

static {
//序列化的时候序列对象的所有属性
objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
//反序列化的时候如果多了其他属性,不抛出异常
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//如果是空对象的时候,不抛异常
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
//取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setDateFormat(dateformat);
}

/**
* 转换为 JSON 字符串
*
* @param obj
* @return
*/
public static <T> String obj2json(T obj) {
if(obj != null){
try {
return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj);
} catch (Exception e) {
LOGGER.error("对象转字符串异常: {}", e);
}
}
return null;
}

/**
* 漂亮的转换为 JSON 字符串
*
* @param obj
* @return
*/
public static <T> String obj2StringPretty(T obj) {
if(obj != null){
try {
return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
} catch (Exception e) {
LOGGER.error("对象转字符串异常: {}", e);
}
}
return null;
}

/**
* 转换为 JavaBean
*
* @param str
* @param clazz
* @return
* @throws Exception
*/
public static <T> T json2pojo(String str, Class<T> clazz) {
if(StringUtils.hasLength(str) && clazz != null){
try {
return clazz.equals(String.class)? (T)str : objectMapper.readValue(str,clazz);
} catch (Exception e) {
LOGGER.error("字符串转对象异常: {}", e);
}
}
return null;
}

/**
* 将 Map 转换为 JavaBean
*
* @param map
* @param clazz
* @return
*/
public static <T> T map2pojo(Map map, Class<T> clazz) {
return objectMapper.convertValue(map, clazz);
}

/**
* 将 JSON 对象转换为 JavaBean
*
* @param obj
* @param clazz
* @return
*/
public static <T> T obj2pojo(Object obj, Class<T> clazz) {
return objectMapper.convertValue(obj, clazz);
}

/**
* 将指定节点的 JSON 数据转换为 JavaBean
*
* @param str
* @param treeNode
* @param clazz
* @return
*/
public static <T> T json2pojoByTree(String str, String treeNode, Class<T> clazz) {
if(StringUtils.hasLength(str) && StringUtils.hasLength(str) && clazz == null){
try {
JsonNode jsonNode = objectMapper.readTree(str);
JsonNode data = jsonNode.findPath(treeNode);
return json2pojo(data.toString(), clazz);
} catch (Exception e) {
LOGGER.error("字符串按节点转对象异常: {}", e);
}
}
return null;
}
}

+ 4
- 0
tuoheng_oidc_server/pom.xml 查看文件

@@ -105,6 +105,10 @@
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>

</dependencies>


+ 1
- 1
tuoheng_oidc_server/src/main/java/com/tuoheng/config/MyCorsFilter.java 查看文件

@@ -29,7 +29,7 @@ public class MyCorsFilter implements Filter {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, authorization");
response.setHeader("Access-Control-Allow-Headers", "*");

if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);

+ 1
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java 查看文件

@@ -119,6 +119,7 @@ public class SecurityConfig {
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/toLogin", "/getHealth", "/static/**", "/vercode").permitAll()
.antMatchers("/user/create", "/user/getInfo").permitAll()
.antMatchers("/third/authorize", "/third/redirect").permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the

+ 32
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/HeadClientHttpRequestInterceptor.java 查看文件

@@ -0,0 +1,32 @@
package com.tuoheng.config.http;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;

/**
* 默认拦截器
*/
public class HeadClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(HeadClientHttpRequestInterceptor.class);

@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
LOGGER.info("#####head handle########");
HttpHeaders headers = httpRequest.getHeaders();
headers.add("Accept", "application/json");
headers.add("Accept-Encoding", "gzip");
headers.add("Content-Encoding", "UTF-8");
headers.add("Content-Type", "application/json; charset=UTF-8");
ClientHttpResponse response = clientHttpRequestExecution.execute(httpRequest, bytes);
HttpHeaders headersResponse = response.getHeaders();
headersResponse.add("Accept", "application/json");
return response;
}
}

+ 64
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/RestProperties.java 查看文件

@@ -0,0 +1,64 @@
package com.tuoheng.config.http;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
@Getter
@Setter
public class RestProperties {

public final static String HTTP = "http";

public final static String HTTPS = "https";

/**
* 连接超时, 默认10秒
*/
@Value("${spring.http.restTemplate.connect.timeout: 10000}")
private Integer connectTimeout;

/**
* 响应超时, 默认2分钟
*/
@Value("${spring.http.restTemplate.read.timeout: 120000}")
private Integer readTimeout;

/**
* 请求超时时间, 默认10秒
*/
@Value("${spring.http.restTemplate.connection.request.timeout: 10000}")
private Integer connectionRequestTimeout;

/**
* 请求失败重试次数, 默认重试3次
*/
@Value("${spring.http.restTemplate.retryCount: 3}")
private Integer retryCount;

/**
* 请求失败重试开关,默认开启
*/
@Value("${spring.http.restTemplate.requestSentRetryEnabled: true}")
private Boolean requestSentRetryEnabled;

/**
* 线程池最大连接数,默认1000
*/
@Value("${spring.http.restTemplate.pool.maxTotal: 1000}")
private Integer maxTotal;

/**
* 线程池主机最大并发数,默认100
*/
@Value("${spring.http.restTemplate.pool.maxPerRoute: 100}")
private Integer maxPerRoute;

/**
* 线程池空闲连接过期时间,默认60秒
*/
@Value("${spring.http.restTemplate.pool.validateAfterInactivity: 60000}")
private Integer validateAfterInactivity;
}

+ 175
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/http/RestTemplateConfig.java 查看文件

@@ -0,0 +1,175 @@
package com.tuoheng.config.http;

import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class RestTemplateConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(RestTemplateConfig.class);

@Bean
public RestTemplate restTemplate(RestProperties restProperties) {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory(restProperties));
// 获取restTemplate中的转换器集合
List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
// 遍历转换器集合,找到对应的StringHttpMessageConverter转换器
for (HttpMessageConverter<?> converter : converterList) {
if (converter.getClass() == StringHttpMessageConverter.class) {
converterList.remove(converter);
break;
}
}
// 添加新的StringHttpMessageConverter转换器,并设置字符集为UTF-8
converterList.add(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 添加拦截器
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>();
// interceptors.add(new DefaultClientHttpRequestInterceptor());
interceptors.add(new HeadClientHttpRequestInterceptor());
restTemplate.setInterceptors(interceptors);
return restTemplate;
}

private ClientHttpRequestFactory clientHttpRequestFactory(RestProperties restProperties) {
// httpClientBuilder配置构架器
HttpClientBuilder httpClientBuilder = HttpClients.custom();
// 设置重试次数,此处注意,如果使用无参构造,重试次数为3。this(3, false);
httpClientBuilder.setRetryHandler(new DefaultHttpRequestRetryHandler(restProperties.getRetryCount(), restProperties.getRequestSentRetryEnabled()));
// 设置保持长连接
httpClientBuilder.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy());
// 使用连接池
httpClientBuilder.setConnectionManager(poolingConnectionManager(restProperties));
// 获取httpClient
CloseableHttpClient httpClient = httpClientBuilder.build();
// 配置HttpClient的对应工厂HttpComponentsClientHttpRequestFactory
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(restProperties.getConnectTimeout());
factory.setReadTimeout(restProperties.getReadTimeout());
factory.setConnectionRequestTimeout(restProperties.getConnectionRequestTimeout());
return factory;
}

private HttpClientConnectionManager poolingConnectionManager(RestProperties restProperties) {
// 注册http和https请求
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register(RestProperties.HTTP, PlainConnectionSocketFactory.getSocketFactory())
.register(RestProperties.HTTPS, createSSLConn())
.build();
PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(registry);
// 最大连接数
poolingConnectionManager.setMaxTotal(restProperties.getMaxTotal());
// 每个主机的并发
poolingConnectionManager.setDefaultMaxPerRoute(restProperties.getMaxPerRoute());
// 空闲连接过期时间
poolingConnectionManager.setValidateAfterInactivity(restProperties.getValidateAfterInactivity());
return poolingConnectionManager;
}

private SSLConnectionSocketFactory createSSLConn() {
SSLConnectionSocketFactory sslsf = null;
try {
SSLContext sslContext = new SSLContextBuilder()
// 不检查证书
.loadTrustMaterial(null, (X509Certificate[] chain, String authType) -> true)
.build();
sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); // 不检查hostname
} catch (GeneralSecurityException e) {
LOGGER.error("restTemplate开启SSL校验失败, error:{}", e);
}
return sslsf;
}


/**
* 自定义的重试策略,需要的时候使用
*/
private void customHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) {
// 请求失败时,进行请求重试
HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException e, int currentRetryCount, HttpContext httpContext) {
if (!requestSentRetryEnabled) {
return false;
}
if (currentRetryCount > retryCount) {
// 重试超过3次,放弃请求
LOGGER.error("retry has more than 3 time, give up request");
return false;
}
if (e instanceof NoHttpResponseException) {
// 服务器没有响应,可能是服务器断开了连接,应该重试
LOGGER.error("receive no response from server, retry");
return true;
}
if (e instanceof SSLHandshakeException) {
// SSL握手异常
LOGGER.error("SSL hand shake exception");
return false;
}
if (e instanceof InterruptedIOException) {
// 超时
LOGGER.error("InterruptedIOException");
return false;
}
if (e instanceof UnknownHostException) {
// 服务器不可达
LOGGER.error("server host unknown");
return false;
}
if (e instanceof ConnectTimeoutException) {
// 连接超时
LOGGER.error("Connection Time out");
return false;
}
if (e instanceof SSLException) {
LOGGER.error("SSLException");
return false;
}
HttpClientContext context = HttpClientContext.adapt(httpContext);
HttpRequest request = context.getRequest();
if (!(request instanceof HttpEntityEnclosingRequest)) {
// 如果请求不是关闭连接的请求
return true;

}
return false;
}
};
}
}

+ 16
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/constants/OidcConstant.java 查看文件

@@ -0,0 +1,16 @@
package com.tuoheng.constants;

import lombok.Data;

/**
* oidc相关授权接口
* @Author xiaoying
* @Date 2023/11/10 13:55
*/
@Data
public class OidcConstant {

public static final String OAUTH2_TOKEN = "/oauth2/token";

public static final String GET_USERINFO = "/user/getInfo";
}

+ 14
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/constants/ThirdConstant.java 查看文件

@@ -0,0 +1,14 @@
package com.tuoheng.constants;

/**
* 第三方接口常量类
*
* @Author xiaoying
* @Date 2023/11/10 9:16
*/
public class ThirdConstant {

public static final String OAUTH_URL = "xxxxx.xxxx";


}

+ 40
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/controller/ThirdController.java 查看文件

@@ -0,0 +1,40 @@
package com.tuoheng.controller;

import com.tuoheng.service.ThirdService;
import com.tuoheng.until.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* 第三方请求响应 前端控制器
*
* @Author xiaoying
* @Date 2023/3/8 9:01
*/
@RestController
@RequestMapping("/third")
@Slf4j
@CrossOrigin(origins = "https://airport-test.t-aaron.com")
public class ThirdController {

@Autowired
private ThirdService thirdService;

@GetMapping("/authorize")
public JsonResult authorize(String clientId) {
return thirdService.authorize(clientId);
}

@GetMapping(value = "/redirect")
public void redirect(HttpServletRequest req, HttpServletResponse resp) throws Exception {
log.info("访问airport");
resp.sendRedirect("https://airport.t-aaron.com/transfer");
}
}

+ 17
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/RegisteredClientMapper.java 查看文件

@@ -0,0 +1,17 @@
package com.tuoheng.mapper;

import com.tuoheng.model.po.RegisteredClientPo;
import com.tuoheng.until.JsonResult;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
* @Author xiaoying
* @Date 2023/11/13 16:56
*/
@Mapper
public interface RegisteredClientMapper {

RegisteredClientPo selectClient(@Param("clientId") String clientId);

}

+ 19
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/RegisteredClientPo.java 查看文件

@@ -0,0 +1,19 @@
package com.tuoheng.model.po;

import lombok.Data;
import lombok.experimental.Accessors;

/**
* @Author xiaoying
* @Date 2023/11/13 16:52
*/
@Data
@Accessors(chain = true)
public class RegisteredClientPo {

private Integer id;

private String clientId;

private String clientSecret;
}

+ 22
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/result/ProfileResult.java 查看文件

@@ -0,0 +1,22 @@
package com.tuoheng.model.result;

import com.tuoheng.model.dto.ClientRoleDto;
import lombok.Data;

import java.util.List;

/**
* @Author xiaoying
* @Date 2023/11/11 13:20
*/
@Data
public class ProfileResult {

private String sub;
private List<String> authority;
private String userId;
private String userName;
private List<ClientRoleDto> clientRoleList;
private String azp;

}

+ 26
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/result/TokenResult.java 查看文件

@@ -0,0 +1,26 @@
package com.tuoheng.model.result;

import com.tuoheng.model.dto.ClientRoleDto;
import lombok.Data;

import java.util.List;

/**
* @Author xiaoying
* @Date 2023/11/10 14:11
*/
@Data
public class TokenResult {

private String access_token;
private String refresh_token;
private String scope;
private String token_type;
private String expires_in;
private String userId;
private String userName;
private String sub;
private List<String> authorityList;
private List<ClientRoleDto> clientRoleDtoList;
private ProfileResult profile;
}

+ 10
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/service/ThirdService.java 查看文件

@@ -0,0 +1,10 @@
package com.tuoheng.service;

import com.tuoheng.until.JsonResult;

import javax.servlet.http.HttpServletRequest;

public interface ThirdService {

JsonResult authorize(String clientId);
}

+ 147
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/ThirdServiceImpl.java 查看文件

@@ -0,0 +1,147 @@
package com.tuoheng.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.tuoheng.constants.OidcConstant;
import com.tuoheng.constants.ThirdConstant;
import com.tuoheng.exception.DiyException;
import com.tuoheng.mapper.RegisteredClientMapper;
import com.tuoheng.model.dto.ClientRoleDto;
import com.tuoheng.model.po.RegisteredClientPo;
import com.tuoheng.model.result.ProfileResult;
import com.tuoheng.model.result.TokenResult;
import com.tuoheng.service.ThirdService;
import com.tuoheng.until.HttpUtils;
import com.tuoheng.until.JsonResult;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.rmi.ServerException;
import java.util.Base64;
import java.util.List;

/**
* @Author xiaoying
* @Date 2023/11/11 10:19
*/
@Service
public class ThirdServiceImpl implements ThirdService {


@Autowired
private RestTemplate restTemplate;

@Value("${oauth2.token.issuer}")
private String tokenIssuer;
@Autowired
private RegisteredClientMapper registeredClientMapper;

private static final String username = "gzjj";

private static final String password = "gzjj@2023";


/**
* 根据第三方授权验证token 进行获取用户信息并跳转相关地址
*
* @param clientId 平台标识
* @return
*/
@Override
public JsonResult authorize(String clientId) {

//通过oidc的密码模式获取授权token 等相关信息数据
TokenResult tokenResult = getToken(clientId);
tokenResult.setUserName(username);
//此时通过token获取当前用户的相关权限信息并进行封装
tokenResult = getClientResult(tokenResult, clientId);
//数据封装完毕返回数据 以及相关地址 -> 是否重定向
return JsonResult.success(tokenResult);
}

/**
* 通过账号密码获取token
*/
private TokenResult getToken(String clientId) {

RegisteredClientPo clientPo = registeredClientMapper.selectClient(clientId);
if (ObjectUtils.isEmpty(clientPo)) {
throw new DiyException("该clientId不存在本系统中");
}

String url = tokenIssuer + OidcConstant.OAUTH2_TOKEN;
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("password", password);
//定死账号密码
params.add("username", username);
params.add("grant_type", "password");
params.add("scope", "openid profile");
//机场标识
String clientSecret = clientPo.getClientSecret().split("}")[1];
String userMsg = clientPo.getClientId() + ":" + clientSecret;
String authorization = Base64.getEncoder().encodeToString(userMsg.getBytes());

ParameterizedTypeReference<TokenResult> parameterizedTypeReference =
new ParameterizedTypeReference<TokenResult>() {
};

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("Authorization", "Basic " + authorization);
HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity(params, headers);

ResponseEntity<TokenResult> response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, parameterizedTypeReference);
return response.getBody();
}

/**
* 获取对应用户的相关信息
*
* @param tokenResult
* @return
*/
private TokenResult getClientResult(TokenResult tokenResult, String clientId) {

String url = tokenIssuer + OidcConstant.GET_USERINFO;

ParameterizedTypeReference<JsonResult<TokenResult>> parameterizedTypeReference =
new ParameterizedTypeReference<JsonResult<TokenResult>>() {
};

HttpHeaders header = new HttpHeaders();
header.add("Authorization", "Bearer " + tokenResult.getAccess_token());
JSONObject object = new JSONObject();
object.put("username", tokenResult.getUserName());
HttpEntity httpEntity = new HttpEntity(object, header);

ResponseEntity<JsonResult<TokenResult>> exchange = restTemplate.exchange(url, HttpMethod.POST, httpEntity, parameterizedTypeReference);

TokenResult result = exchange.getBody().getData();
//封装数据
List<String> authorityList = result.getAuthorityList();
List<ClientRoleDto> clientRoleDtoList = result.getClientRoleDtoList();
tokenResult.setAuthorityList(authorityList);
tokenResult.setClientRoleDtoList(clientRoleDtoList);
tokenResult.setSub(result.getUserName());
tokenResult.setUserId(result.getUserId());
tokenResult.setUserName(result.getUserName());
ProfileResult profile = new ProfileResult();
BeanUtils.copyProperties(tokenResult, profile);
profile.setSub(username);
profile.setAzp(clientId);
profile.setAuthority(authorityList);
profile.setClientRoleList(clientRoleDtoList);
tokenResult.setProfile(profile);

return tokenResult;

}
}

+ 242
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/until/HttpUtils.java 查看文件

@@ -0,0 +1,242 @@
package com.tuoheng.until;

import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.*;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
* 通用http发送方法
*/
public class HttpUtils {

private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);

/**
* 向指定 URL 发送GET方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendGet(String url, String param) {
StringBuilder result = new StringBuilder();
BufferedReader in = null;
try {
String urlNameString = url + "?" + param;
log.info("sendGet - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection connection = realUrl.openConnection();
connection.setRequestProperty("accept", "*/*");
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
connection.connect();
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (in != null) {
in.close();
}
} catch (Exception ex) {
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}

/**
* 向指定 URL 发送POST方法的请求
*
* @param url 发送请求的 URL
* @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, String param) {
PrintWriter out = null;
BufferedReader in = null;
StringBuilder result = new StringBuilder();
try {
String urlNameString = url + "?" + param;
log.info("sendPost - {}", urlNameString);
URL realUrl = new URL(urlNameString);
URLConnection conn = realUrl.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);
out = new PrintWriter(conn.getOutputStream());
out.print(param);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
String line;
while ((line = in.readLine()) != null) {
result.append(line);
}
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
}
}
return result.toString();
}

/**
* 处理post请求
*
* @param urlStr 请求url
* @param data 请求体数据
* @param properties 请求头参数
* @return
*/
public static String doSend(String urlStr, JSONObject data, Map<String, String> properties, String method) {
StringBuilder result = new StringBuilder();
try {
log.info("请求url={}", urlStr);
log.info("请求体数据data={}", data.toJSONString());
log.info("请求头参数properties={}", properties);
URL url = new URL(urlStr);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setDoInput(true); // 设置可输入
connection.setDoOutput(true); // 设置该连接是可以输出的
//设置连接超时时间和读取超时时间
connection.setConnectTimeout(15000);
connection.setReadTimeout(60000 * 5);
//设置请求方式
connection.setRequestMethod(method);
connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
if (null != properties && !properties.isEmpty()) {
for (String key : properties.keySet()) {
connection.setRequestProperty(key, properties.get(key));
}
}

PrintWriter pw = new PrintWriter(new BufferedOutputStream(connection.getOutputStream()));
pw.write(data.toJSONString());
pw.flush();
pw.close();

BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
String line = null;

while ((line = br.readLine()) != null) { // 读取数据
result.append(line).append("\n");
}
connection.disconnect();
log.info("recv - {}", result);
} catch (ConnectException e) {
log.error("调用HttpUtils.sendGet ConnectException, url=" + urlStr + ",param=" + data, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + urlStr + ",param=" + data, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendGet IOException, url=" + urlStr + ",param=" + data, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendGet Exception, url=" + urlStr + ",param=" + data, e);
}
return result.toString();
}

public static String sendSSLPost(String url, String param) {
StringBuilder result = new StringBuilder();
String urlNameString = url + "?" + param;
try {
log.info("sendSSLPost - {}", urlNameString);
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
URL console = new URL(urlNameString);
HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
conn.setRequestProperty("accept", "*/*");
conn.setRequestProperty("connection", "Keep-Alive");
conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("contentType", "utf-8");
conn.setDoOutput(true);
conn.setDoInput(true);

conn.setSSLSocketFactory(sc.getSocketFactory());
conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
conn.connect();
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String ret = "";
while ((ret = br.readLine()) != null) {
if (ret != null && !ret.trim().equals("")) {
result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
}
}
log.info("recv - {}", result);
conn.disconnect();
br.close();
} catch (ConnectException e) {
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
} catch (SocketTimeoutException e) {
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
} catch (IOException e) {
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
} catch (Exception e) {
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
}
return result.toString();
}

private static class TrustAnyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {
}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {
}

@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}

private static class TrustAnyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}

}

+ 11
- 0
tuoheng_oidc_server/src/main/resources/mapper/RegisteredClientMapper.xml 查看文件

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.tuoheng.mapper.RegisteredClientMapper">


<select id="selectClient" resultType="com.tuoheng.model.po.RegisteredClientPo">
select id, client_id clientId, client_secret clientSecret
from tuoheng_oidc.oauth2_registered_client
where client_id = #{clientId}
</select>
</mapper>

Loading…
取消
儲存