@@ -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); |
@@ -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; | |||
} | |||
} |
@@ -105,6 +105,10 @@ | |||
<artifactId>fastjson</artifactId> | |||
<version>1.2.76</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.httpcomponents</groupId> | |||
<artifactId>httpclient</artifactId> | |||
</dependency> | |||
</dependencies> | |||
@@ -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); |
@@ -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 |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} | |||
}; | |||
} | |||
} |
@@ -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"; | |||
} |
@@ -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"; | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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; | |||
} |
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} |
@@ -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> |