This commit is contained in:
parent
f91d65803e
commit
fe56edd54f
|
|
@ -0,0 +1,70 @@
|
|||
package com.tuoheng.gateway.filter;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
|
||||
@Component
|
||||
public class JwtPermissionFilter implements GlobalFilter, Ordered {
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
|
||||
|
||||
// 获取完整的请求URL
|
||||
String fullUrl = exchange.getRequest().getURI().toString();
|
||||
System.out.println("用户访问的完整URL: " + fullUrl);
|
||||
|
||||
// 获取请求的path
|
||||
String path = exchange.getRequest().getPath().toString();
|
||||
System.out.println("用户访问的path: " + path);
|
||||
|
||||
// 获取请求的host
|
||||
String host = exchange.getRequest().getHeaders().getHost().toString();
|
||||
System.out.println("用户访问的host: " + host);
|
||||
|
||||
String hostName = exchange.getRequest().getHeaders().getHost().getHostName();
|
||||
System.out.println("用户访问的域名: " + hostName);
|
||||
|
||||
// 获取Referer(如果有)
|
||||
String referer = exchange.getRequest().getHeaders().getFirst("Referer");
|
||||
System.out.println("Referer: " + referer);
|
||||
// 从Spring Security上下文获取JWT
|
||||
return ReactiveSecurityContextHolder.getContext()
|
||||
.flatMap(securityContext -> {
|
||||
if (securityContext.getAuthentication() instanceof JwtAuthenticationToken) {
|
||||
JwtAuthenticationToken jwtAuth = (JwtAuthenticationToken) securityContext.getAuthentication();
|
||||
Jwt jwt = jwtAuth.getToken();
|
||||
String username = jwt.getClaimAsString("username");
|
||||
String clientId = jwt.getClaimAsString("client_id");
|
||||
String tenantCode = jwt.getClaimAsString("tenant_code");
|
||||
String authorities = jwt.getClaimAsString("clientIds");
|
||||
|
||||
// 你可以在这里做权限判断
|
||||
System.out.println("网关解析到token字段:");
|
||||
System.out.println("用户名: " + username);
|
||||
System.out.println("客户端ID: " + clientId);
|
||||
System.out.println("租户代码: " + tenantCode);
|
||||
System.out.println("用户权限: " + authorities);
|
||||
|
||||
//该域名没有权限
|
||||
if(!authorities.contains(hostName)){
|
||||
exchange.getResponse().setStatusCode(org.springframework.http.HttpStatus.FORBIDDEN);
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1; // 优先级高
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
package com.tuoheng.oauth.oidc.db;
|
||||
|
||||
import com.tuoheng.oauth.oidc.db.entity.*;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class DbService {
|
||||
|
||||
List<Client> clients = new ArrayList<>();
|
||||
List<Tenant> tenants = new ArrayList<>();
|
||||
List<TenantClient> tenantClients = new ArrayList<>();
|
||||
List<com.tuoheng.oauth.oidc.db.entity.User> users = new ArrayList<>();
|
||||
List<UserClientAuthorities> userClientAuthorities = new ArrayList<>();
|
||||
|
||||
// 构造函数初始化一些数据
|
||||
public DbService() {
|
||||
|
||||
// 添加租户
|
||||
tenants.add(new Tenant(1L, "t1"));
|
||||
tenants.add(new Tenant(2L, "t2"));
|
||||
|
||||
// 添加客户端
|
||||
clients.add(new Client(1L, "a-client","a-secret","https://a.local.com/callback"));
|
||||
clients.add(new Client(2L, "b-client","b-secret","https://b.local.com/callback"));
|
||||
|
||||
// 租户-客户端关系
|
||||
tenantClients.add(new TenantClient(1L, 1L,1L));
|
||||
tenantClients.add(new TenantClient(2L, 1L,2L));
|
||||
tenantClients.add(new TenantClient(3L, 2L,1L));
|
||||
|
||||
// 为租户添加用户
|
||||
users.add(new User(1L,1L,"u1","u1",true,false));
|
||||
users.add(new User(2L,1L,"u2","u2",false,false));
|
||||
users.add(new User(3L,2L,"u3","u3",false,false));
|
||||
|
||||
// 添加系统的普通用户
|
||||
userClientAuthorities.add(new UserClientAuthorities(1L,2L,1L));
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class UserInfo {
|
||||
public String userName;
|
||||
public String password;
|
||||
public List<String> validClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断租户是否有权限
|
||||
* @param clientId
|
||||
* @param tenantName
|
||||
* @return
|
||||
*/
|
||||
public Boolean isValidTenant(String clientId,String tenantName) {
|
||||
// 先找到tenantId和clientId
|
||||
Tenant tenant = tenants.stream()
|
||||
.filter(t -> t.getName().equals(tenantName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Client client = clients.stream()
|
||||
.filter(c -> c.getClientId().equals(clientId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (tenant == null || client == null) return false;
|
||||
// 判断租户-客户端关系
|
||||
return tenantClients.stream()
|
||||
.anyMatch(tc -> tc.getTenantId().equals(tenant.getId()) && tc.getClientId().equals(client.getId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是有权限的客户端
|
||||
* @param clientId
|
||||
* @return
|
||||
*/
|
||||
public Boolean isValidClientId(String clientId) {
|
||||
return clients.stream()
|
||||
.anyMatch(c -> c.getClientId().equals(clientId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否有权限
|
||||
* @param clientId
|
||||
* @param tenantName
|
||||
* @param userName
|
||||
* @return
|
||||
*/
|
||||
public Boolean isValidUserId(String clientId,String tenantName,String userName) {
|
||||
// 先找到tenantId和clientId
|
||||
Tenant tenant = tenants.stream()
|
||||
.filter(t -> t.getName().equals(tenantName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Client client = clients.stream()
|
||||
.filter(c -> c.getClientId().equals(clientId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (tenant == null || client == null) return false;
|
||||
// 找到用户
|
||||
User user = users.stream()
|
||||
.filter(u -> u.getUserName().equals(userName) && u.getTenentId().equals(tenant.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (user == null) return false;
|
||||
|
||||
//如果是租户用户,就有权限,租户用户的权限和租户的权限一致
|
||||
if(user.getTenantUser()){
|
||||
return true;
|
||||
}
|
||||
|
||||
//判断用户是否有该client的权限
|
||||
return userClientAuthorities.stream()
|
||||
.anyMatch(uca -> uca.getUserId().equals(user.getId()) && uca.getClientId().equals(client.getId()));
|
||||
}
|
||||
|
||||
|
||||
public UserInfo getUser(String clientId, String tenantName, String userName) {
|
||||
Tenant tenant = tenants.stream()
|
||||
.filter(t -> t.getName().equals(tenantName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
Client client = clients.stream()
|
||||
.filter(c -> c.getClientId().equals(clientId))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (tenant == null || client == null) return null;
|
||||
User user = users.stream()
|
||||
.filter(u -> u.getUserName().equals(userName) && u.getTenentId().equals(tenant.getId()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (user == null) return null;
|
||||
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.userName = user.getUserName();
|
||||
userInfo.password = user.getPassword();
|
||||
|
||||
//如果是租户用户
|
||||
if(user.getTenantUser()){
|
||||
//todo 获取租户有权限的client的所有 clientUrl 字段赋予 userInfo.validClient
|
||||
List<Long> clientIds = tenantClients.stream()
|
||||
.filter(tc -> tc.getTenantId().equals(tenant.getId()))
|
||||
.map(tc -> tc.getClientId())
|
||||
.toList();
|
||||
|
||||
List<String> clientUrls = clients.stream()
|
||||
.filter(c -> clientIds.contains(c.getId()))
|
||||
.map(c -> c.getClientUrl()) // 如果你的字段名是clientUrl请改成c.clientUrl
|
||||
.toList();
|
||||
|
||||
userInfo.validClient = clientUrls;
|
||||
|
||||
}else {
|
||||
//todo 获取有权限的client的所有 clientUrl 字段赋予 userInfo.validClient
|
||||
List<Long> userClientIds = userClientAuthorities.stream()
|
||||
.filter(uca -> uca.getUserId().equals(user.getId()))
|
||||
.map(uca -> uca.getClientId())
|
||||
.toList();
|
||||
|
||||
List<String> clientUrls = clients.stream()
|
||||
.filter(c -> userClientIds.contains(c.getId()))
|
||||
.map(c -> c.getClientUrl()) // 如果你的字段名是clientUrl请改成c.clientUrl
|
||||
.toList();
|
||||
|
||||
userInfo.validClient = clientUrls;
|
||||
}
|
||||
|
||||
return userInfo;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package com.tuoheng.oauth.oidc.db.entity;
|
||||
|
||||
|
||||
public class Client {
|
||||
|
||||
public Client(Long id, String clientId, String clientSecret, String clientUrl) {
|
||||
this.id = id;
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.clientUrl = clientUrl;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public String getClientSecret() {
|
||||
return clientSecret;
|
||||
}
|
||||
|
||||
public void setClientSecret(String clientSecret) {
|
||||
this.clientSecret = clientSecret;
|
||||
}
|
||||
|
||||
public String getClientUrl() {
|
||||
return clientUrl;
|
||||
}
|
||||
|
||||
public void setClientUrl(String clientUrl) {
|
||||
this.clientUrl = clientUrl;
|
||||
}
|
||||
|
||||
private Long id;
|
||||
private String clientId;
|
||||
private String clientSecret;
|
||||
private String clientUrl;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tuoheng.oauth.oidc.db.entity;
|
||||
|
||||
public class Tenant {
|
||||
|
||||
public Tenant(Long id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库主键
|
||||
*/
|
||||
private Long id;
|
||||
private String name;
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tuoheng.oauth.oidc.db.entity;
|
||||
|
||||
public class TenantClient {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public TenantClient(Long id, Long tenantId, Long clientId) {
|
||||
this.id = id;
|
||||
this.tenantId = tenantId;
|
||||
this.clientId = clientId;
|
||||
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(Long clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public void setTenantId(Long tenantId) {
|
||||
this.tenantId = tenantId;
|
||||
}
|
||||
|
||||
private Long id;
|
||||
private Long clientId;
|
||||
private Long tenantId;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package com.tuoheng.oauth.oidc.db.entity;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class User implements Serializable {
|
||||
|
||||
public User(Long id, Long tenantId, String userName, String password, Boolean isTenantUser ,Boolean isLongToken ) {
|
||||
this.id = id;
|
||||
this.tenentId = tenantId;
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
this.isTenantUser = isTenantUser;
|
||||
this.isLongToken = isLongToken;
|
||||
}
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTenentId() {
|
||||
return tenentId;
|
||||
}
|
||||
|
||||
public void setTenentId(Long tenentId) {
|
||||
this.tenentId = tenentId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Boolean getTenantUser() {
|
||||
return isTenantUser;
|
||||
}
|
||||
|
||||
public void setTenantUser(Boolean tenantUser) {
|
||||
isTenantUser = tenantUser;
|
||||
}
|
||||
|
||||
public Boolean getLongToken() {
|
||||
return isLongToken;
|
||||
}
|
||||
|
||||
public void setLongToken(Boolean longToken) {
|
||||
isLongToken = longToken;
|
||||
}
|
||||
|
||||
private Long id;
|
||||
private Long tenentId;
|
||||
private String userName;
|
||||
private String password;
|
||||
private Boolean isTenantUser;
|
||||
private Boolean isLongToken;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tuoheng.oauth.oidc.db.entity;
|
||||
|
||||
public class UserClientAuthorities {
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public UserClientAuthorities(Long id, Long userId, Long clientId) {
|
||||
this.id = id;
|
||||
this.userId = userId;
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(Long userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public Long getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(Long clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
private Long id;
|
||||
private Long userId;
|
||||
private Long clientId;
|
||||
}
|
||||
|
|
@ -45,7 +45,9 @@ public class TenantAwareAuthenticationProvider implements AuthenticationProvider
|
|||
System.out.println("客户端ID: " + clientId);
|
||||
System.out.println("租户代码: " + tenantCode);
|
||||
|
||||
//在这边判断用户是否有权限
|
||||
/**
|
||||
* 这边判断用户是否有权限
|
||||
*/
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username,clientId,tenantCode);
|
||||
|
||||
|
||||
|
|
@ -57,6 +59,8 @@ public class TenantAwareAuthenticationProvider implements AuthenticationProvider
|
|||
Map<String, Object> details = new HashMap<>();
|
||||
details.put("client_id", clientId);
|
||||
details.put("tenant_code", tenantCode);
|
||||
details.put("clientIds",userDetails.getAuthorities().toString());
|
||||
|
||||
token.setDetails(details);
|
||||
return token;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tuoheng.oauth.oidc.service;
|
||||
|
||||
import com.tuoheng.oauth.oidc.db.DbService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
public class CustomUserDetailsService implements UserDetailsService {
|
||||
|
|
@ -16,6 +16,9 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||
@Autowired
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private DbService dbService;
|
||||
|
||||
|
||||
/**
|
||||
* 这个地方需要扩展,判断用户是否有权限
|
||||
|
|
@ -26,15 +29,36 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||
* @throws UsernameNotFoundException
|
||||
*/
|
||||
public UserDetails loadUserByUsername(String username,String clientId,String tenantCode) throws UsernameNotFoundException {
|
||||
return org.springframework.security.core.userdetails.User.builder()
|
||||
.username("1")
|
||||
.password(passwordEncoder.encode("1"))
|
||||
.authorities(new HashSet<>())
|
||||
|
||||
if(!dbService.isValidClientId(clientId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!dbService.isValidTenant(clientId,tenantCode)){
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!dbService.isValidUserId(clientId,tenantCode,username)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DbService.UserInfo userInfo = dbService.getUser(clientId,tenantCode,username);
|
||||
|
||||
if(Objects.nonNull(userInfo)) {
|
||||
String[] authorities = userInfo.validClient.toArray(new String[0]);
|
||||
return org.springframework.security.core.userdetails.User.builder()
|
||||
.username(userInfo.userName)
|
||||
.password(passwordEncoder.encode(userInfo.password))
|
||||
.authorities(authorities)
|
||||
.accountExpired(false)
|
||||
.accountLocked(false)
|
||||
.credentialsExpired(false)
|
||||
.disabled(false)
|
||||
.build();
|
||||
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,16 @@ public class CustomTokenCustomizer implements OAuth2TokenCustomizer<JwtEncodingC
|
|||
if (detailsObj instanceof Map details) {
|
||||
Object clientId = details.get("client_id");
|
||||
Object tenantCode = details.get("tenant_code");
|
||||
Object clientIds = details.get("clientIds");
|
||||
if (clientId != null) {
|
||||
context.getClaims().claim("client_id", clientId.toString());
|
||||
}
|
||||
if (tenantCode != null) {
|
||||
context.getClaims().claim("tenant_code", tenantCode.toString());
|
||||
}
|
||||
if(clientIds != null) {
|
||||
context.getClaims().claim("clientIds", clientIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue