ソースを参照

Merge branch 'release' of gitadmin/tuoheng_gateway into master

gitadmin-patch-1
gitadmin 1年前
コミット
549aae8933
11個のファイルの変更383行の追加7行の削除
  1. +19
    -0
      pom.xml
  2. +210
    -0
      src/main/java/com/tuoheng/gateway/config/GatewayFilterConfig.java
  3. +31
    -0
      src/main/java/com/tuoheng/gateway/config/TokenConfig.java
  4. +4
    -5
      src/main/java/com/tuoheng/gateway/config/WebSecurityConfig.java
  5. +6
    -0
      src/main/java/com/tuoheng/gateway/constants/AuthorityConstant.java
  6. +19
    -0
      src/main/java/com/tuoheng/gateway/model/ClientUserRoleDto.java
  7. +64
    -0
      src/main/java/com/tuoheng/gateway/ustil/EncryptUtil.java
  8. +9
    -2
      src/main/resources/application-dev.yml
  9. +7
    -0
      src/main/resources/application-local.yml
  10. +7
    -0
      src/main/resources/application-prod.yml
  11. +7
    -0
      src/main/resources/application-test.yml

+ 19
- 0
pom.xml ファイルの表示

@@ -27,6 +27,19 @@
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
<version>3.1.1</version>
</dependency>

<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.0</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
@@ -68,6 +81,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>

</dependencies>

<!-- 构建环境变量 -->

+ 210
- 0
src/main/java/com/tuoheng/gateway/config/GatewayFilterConfig.java ファイルの表示

@@ -0,0 +1,210 @@
package com.tuoheng.gateway.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.auth0.jwt.JWT;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.tuoheng.gateway.model.ClientUserRoleDto;
import com.tuoheng.gateway.ustil.EncryptUtil;
import io.micrometer.core.instrument.util.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.*;


@Configuration
public class GatewayFilterConfig implements GlobalFilter, Ordered {

private static final String USERNAME = "username";

private static final String OUSERID = "oUserId";

private static final String SCOPE = "scope";

private static final String CLIENTROLELIST = "clientRoleList";

private static final String ADMIN = "admin";


@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//todo:header里封装 Client-Id 信息
/*String clientId = getClientId(exchange);
if(StringUtils.isEmpty(clientId)){
return invalidClientIdMono(exchange);
}
String requestUrl = exchange.getRequest().getPath().value();
//todo:获取当前系统、当前接口 可以访问的角色集合 start
Map<String, List<Integer>> permissionMap = getPermissionByClientId(clientId);
List<Integer> roleIds = permissionMap.get(requestUrl);*/
//todo:获取当前系统、当前接口 可以访问的角色集合 end
String token = getToken(exchange);
String username = null;
Long oUserId = null;
List<String> authorityList = new ArrayList<>();
List<ClientUserRoleDto> clientUserRoleDtoList = new ArrayList<>();
if (!StringUtils.isBlank(token)) {
//token数据解析
DecodedJWT decodedJWT = JWT.decode(token);
username = decodedJWT.getClaim(USERNAME).asString();
oUserId = decodedJWT.getClaim(OUSERID).asLong();
authorityList = decodedJWT.getClaim(SCOPE).asList(String.class);
clientUserRoleDtoList = decodedJWT.getClaim(CLIENTROLELIST).asList(ClientUserRoleDto.class);
}
/*if(roleIds != null){
//说明这个url 需要一定的角色才可以访问
//在不是admin权限的情况下进行校验
if(!authorityList.contains(ADMIN)){
//获取用户 client_id 对应的 roleId
ClientUserRoleDto clientUserRoleDto = clientUserRoleDtoList.stream().filter(dto -> dto.getClientId().equals(clientId))
.findFirst().orElse(null);
if(Objects.isNull(clientUserRoleDto)){
return forbiddenTokenMono(exchange);
}
Integer roleId = clientUserRoleDto.getRoleId();
if(!roleIds.contains(roleId)){
return forbiddenTokenMono(exchange);
}
}
}*/

if (!StringUtils.isBlank(token)) {
JSONObject jsonObject = new JSONObject();
jsonObject.put(USERNAME, username);
jsonObject.put(OUSERID, oUserId);
String base64 = EncryptUtil.encodeUTF8StringBase64(jsonObject.toJSONString());
try {
ServerHttpRequest tokenRequest = exchange.getRequest().mutate().header("th-token", token)
.header("o-user-json", base64)
.build();
ServerWebExchange build = exchange.mutate().request(tokenRequest).build();
return chain.filter(build);
} catch (InvalidTokenException e) {
return invalidTokenMono(exchange);
}
}
return chain.filter(exchange);
}


/**
* 获取token
*/
private String getToken(ServerWebExchange exchange) {
try {
String tokenStr = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isBlank(tokenStr)) {
return null;
}
if (!tokenStr.startsWith("Bearer ")) {
return null;
}
String token = tokenStr.split("Bearer ")[1];
if (StringUtils.isBlank(token)) {
return null;
}
return token;
} catch (Exception e) {
return null;
}
}

private String getClientId(ServerWebExchange exchange) {
try {
String clientIdStr = exchange.getRequest().getHeaders().getFirst("Client-Id");
if (StringUtils.isBlank(clientIdStr)) {
return null;
}
return clientIdStr;
} catch (Exception e) {
return null;
}
}


/**
* 401 无效的token
*/
private Mono<Void> invalidTokenMono(ServerWebExchange exchange) {
JSONObject json = new JSONObject();
json.put("code", HttpStatus.UNAUTHORIZED.value());
json.put("msg", "无效的授权Authorization信息");
json.put("data", null);
return buildReturnMono(json, exchange, HttpStatus.UNAUTHORIZED);
}

/**
* 401 无效的clientId
*/
private Mono<Void> invalidClientIdMono(ServerWebExchange exchange) {
JSONObject json = new JSONObject();
json.put("code", HttpStatus.UNAUTHORIZED.value());
json.put("msg", "无效的Client-Id信息");
json.put("data", null);
return buildReturnMono(json, exchange, HttpStatus.UNAUTHORIZED);
}

/**
* 403 未授权的token
*/
private Mono<Void> forbiddenTokenMono(ServerWebExchange exchange) {
JSONObject json = new JSONObject();
json.put("code", HttpStatus.FORBIDDEN.value());
json.put("msg", "暂无权限访问");
json.put("data", null);
return buildReturnMono(json, exchange, HttpStatus.FORBIDDEN);
}


private Mono<Void> noTokenMono(ServerWebExchange exchange) {
JSONObject json = new JSONObject();
json.put("code", HttpStatus.UNAUTHORIZED.value());
json.put("msg", "未获取到请求头授权Authorization信息");
json.put("data", null);
return buildReturnMono(json, exchange, HttpStatus.UNAUTHORIZED);
}


private Mono<Void> buildReturnMono(JSONObject json, ServerWebExchange exchange, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
byte[] bits = JSONObject.toJSONString(json, SerializerFeature.WriteMapNullValue).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(httpStatus);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset:utf-8");
return response.writeWith(Mono.just(buffer));
}

/**
* 根据 clientId 从业务系统获取 permission - role 数据
* @return
*/
private Map<String, List<Integer>> getPermissionByClientId(String clientId){
// permissionUrl - roleIdList
Map<String, List<Integer>> map = new HashMap<>();
if(clientId.equals("tuoheng-oidc-admin")){
List<Integer> roleIds = new ArrayList<>();
roleIds.add(1001);
roleIds.add(1002);
map.put("/oidc/admin/user/create", roleIds);
}
return map;
}

@Override
public int getOrder() {
return 0;
}
}

+ 31
- 0
src/main/java/com/tuoheng/gateway/config/TokenConfig.java ファイルの表示

@@ -0,0 +1,31 @@
package com.tuoheng.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
public class TokenConfig {

/**
* 秘钥串
*/
private static final String SIGNING_KEY = "uaa";


@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY);
return converter;
}


}

+ 4
- 5
src/main/java/com/tuoheng/gateway/config/WebSecurityConfig.java ファイルの表示

@@ -66,11 +66,10 @@ public class WebSecurityConfig {
String[] PERMIT_PATH = permitUrlStr.split(",");
httpSecurity
.authorizeExchange()
.pathMatchers("/api/system/demo/admin").hasAuthority(AuthorityConstant.SCOPE_ADMIN)
.pathMatchers("/api/system/demo/dsp").hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_DSP_MP)
.pathMatchers("/api/system/demo/hhz").hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_DSP_WEB)
.pathMatchers(OAUTH_PATH).hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_DSP_MP, AuthorityConstant.SCOPE_TUOHNEG_DSP_WEB)
//.pathMatchers("/api/system/**").hasAnyRole("ROLE_ADMIN", "ROLE_DSP")
.pathMatchers(OAUTH_PATH).hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_DSP_MP, AuthorityConstant.SCOPE_TUOHNEG_DSP_WEB)
.pathMatchers("/pilot/miniprogram/**").hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_PILOT_MP)
.pathMatchers("/pilot/admin/**").hasAnyAuthority(AuthorityConstant.SCOPE_ADMIN, AuthorityConstant.SCOPE_TUOHNEG_PILOT_ADMIN)
.pathMatchers("/oidc/admin/**").authenticated()
//.pathMatchers(PERMIT_PATH).permitAll()
.anyExchange().permitAll()
.and()

+ 6
- 0
src/main/java/com/tuoheng/gateway/constants/AuthorityConstant.java ファイルの表示

@@ -20,6 +20,12 @@ public class AuthorityConstant {
*/
public static final String SCOPE_TUOHNEG_DSP_WEB = "SCOPE_tuoheng-dsp-web";

public static final String SCOPE_TUOHNEG_PILOT_ADMIN = "SCOPE_tuoheng-pilot-admin";

public static final String SCOPE_TUOHNEG_PILOT_MP = "SCOPE_tuoheng-pilot-mp";

public static final String SCOPE_TUOHNEG_OIDC_ADMIN = "SCOPE_tuoheng-oidc-admin";

/**
* 河湖长用户权限
*/

+ 19
- 0
src/main/java/com/tuoheng/gateway/model/ClientUserRoleDto.java ファイルの表示

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

import lombok.Data;

import javax.annotation.sql.DataSourceDefinition;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/31 9:36
*/
@Data
public class ClientUserRoleDto {

private String clientId;

private Integer roleId;

}

+ 64
- 0
src/main/java/com/tuoheng/gateway/ustil/EncryptUtil.java ファイルの表示

@@ -0,0 +1,64 @@
package com.tuoheng.gateway.ustil;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
* 加密工具类
* @Author: rosh
*/
public final class EncryptUtil {

private EncryptUtil() {

}

private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);

public static String encodeBase64(byte[] bytes) {
return Base64.getEncoder().encodeToString(bytes);
}

public static byte[] decodeBase64(String str) {

return Base64.getDecoder().decode(str);
}

public static String encodeUTF8StringBase64(String str) {
return Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8));
}

public static String decodeUTF8StringBase64(String str) {
byte[] bytes = Base64.getDecoder().decode(str);
return new String(bytes, StandardCharsets.UTF_8);
}

public static String encodeURL(String url) {
String encoded = null;
try {
encoded = URLEncoder.encode(url, String.valueOf(StandardCharsets.UTF_8));
} catch (UnsupportedEncodingException e) {
logger.warn("URLEncode失败", e);
}
return encoded;
}


public static String decodeURL(String url) {
String decoded = null;
try {
decoded = URLDecoder.decode(url, String.valueOf(StandardCharsets.UTF_8));
} catch (UnsupportedEncodingException e) {
logger.warn("URLDecode失败", e);
}
return decoded;
}

}

+ 9
- 2
src/main/resources/application-dev.yml ファイルの表示

@@ -3,8 +3,8 @@ spring:
oauth2:
resource-server:
jwt:
#issuer-uri: http://192.168.11.11:8090
issuer-uri: http://oidc.dev.t-aaron.com
issuer-uri: http://192.168.11.11:8090
#issuer-uri: http://oidc.dev.t-aaron.com
cloud:
consul:
host: 192.168.11.13 # consul 所在服务地址
@@ -123,6 +123,13 @@ spring:
- Path=/pilot/web/**
filters:
- StripPrefix=2
# oidc admin服务
- id: tuoheng-oidc-admin
uri: lb://tuoheng-oidc-admin
predicates:
- Path=/oidc/admin/**
filters:
- StripPrefix=2
# Redis数据源
redis:
# 缓存库默认索引0

+ 7
- 0
src/main/resources/application-local.yml ファイルの表示

@@ -122,6 +122,13 @@ spring:
- Path=/pilot/web/**
filters:
- StripPrefix=2
# oidc admin服务
- id: tuoheng-oidc-admin
uri: lb://tuoheng-oidc-admin
predicates:
- Path=/oidc/admin/**
filters:
- StripPrefix=2
# Redis数据源
redis:
# 缓存库默认索引0

+ 7
- 0
src/main/resources/application-prod.yml ファイルの表示

@@ -115,6 +115,13 @@ spring:
- Path=/pilot/web/**
filters:
- StripPrefix=2
# oidc admin服务
- id: tuoheng-oidc-admin
uri: lb://tuoheng-oidc-admin
predicates:
- Path=/oidc/admin/**
filters:
- StripPrefix=2
# Redis数据源
redis:
# 缓存库默认索引0

+ 7
- 0
src/main/resources/application-test.yml ファイルの表示

@@ -116,6 +116,13 @@ spring:
- Path=/pilot/web/**
filters:
- StripPrefix=2
# oidc admin服务
- id: tuoheng-oidc-admin
uri: lb://tuoheng-oidc-admin
predicates:
- Path=/oidc/admin/**
filters:
- StripPrefix=2
# Redis数据源
redis:
# 缓存库默认索引0

読み込み中…
キャンセル
保存