我目前已设置Spring Cloud Gateway反向代理,其目的是:
a)处理多个OAuth / OIDC提供程序的身份验证,包括获取令牌
b)在本地从提供程序中查找详细信息,确保已授权OAuth用户x Oauth提供程序组合
c)如果获得授权,请查找“授予/权限”,然后将请求转发给SCG,并在JWT中包含授权主体的详细信息。
d)如果未获得授权,则显示一个页面,其中显示OAuth2 Auth中的相关详细信息,并说明未授权。
我已经完成了大多数步骤,但是在将步骤c)合并到Spring Security Webflux中时遇到了困难
我要做的是获取从Authentication交换获得的OAuth2AuthenticationToken,在步骤中执行查找,然后返回 根据结果定制Prinicipal。
然后通过代码将其用于触发SCG行为或显示页面。
spring-boot.version> 2.1.6。发布 spring-cloud.version>格林尼治SR2
我的问题是我不知道这样做的最佳方法。
在OAuth2客户端中使用一些挂钩执行额外的身份验证步骤。在这种情况下,我可能需要返回OAuth2Principal
在身份验证之后,将额外的安全筛选器添加到链中。 这将用我自己的原则代替OAuth2Principal。我不确定身份验证后替换Principal是否合法,可能会删除身份验证状态
编写一个自定义的AuthN Provider,该代理将代理到OAuth客户端,并且一旦竞争就运行了它自己的逻辑,然后发出信号对其进行身份验证。这似乎是一种复杂的方法,而且我不确定要为此使用什么类。
我已经阅读了Spring Security文档,并了解了Spring Security的一般体系结构,但是无法找到解决此问题的最佳方法。
这是我的春季安全过滤器逻辑
@EnableWebFluxSecurity
public class SecurityConfig {
@Bean
@Profile("oauth")
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
return addAuthZ(http)
.oauth2Login()
.and().build();
}
private ServerHttpSecurity addAuthZ(ServerHttpSecurity http) {
return http.authorizeExchange()
.anyExchange().authenticated().and();
}
}
这是配置,我正在使用示例OAuth2提供程序Google和Facebook,以及使用CAS提供的自定义OAuth2提供程序
spring:
security:
oauth2:
client:
registration:
google:
client-id: SET_ME
client-secret: SET_ME
facebook:
client-id: SET_ME
client-secret: SET_ME
sgd-authn:
provider: sgd-authn
client-id: SET_ME
client-secret: SET_ME
scope: openid
client-authentication-method: secret
authorization-grant-type: authorization_code
#redirect-uri: "{baseUrl}/oauth2/
redirect-uri-template: "{baseUrl}/{action}/oauth2/code/{registrationId}"
provider:
# These are needed for talking to CAS OIDC
sgd-authn:
authorization-uri: ${cas.url}/oidc/authorize
token-uri: ${cas.url}/oidc/accessToken
jwk-set-uri: ${cas.url}/oidc/jwks
user-info-uri: ${cas.url}/oidc/profile
user-name-attribute: sub
答案 0 :(得分:0)
好吧,我设法通过选项2使工作正常,在身份验证之后添加了过滤器。
编写一个过滤器,该过滤器将采用身份验证(OAuth),通过某种逻辑运行该过滤器,并返回一个新的身份验证对象,该对象是特定超类的实例
如果我们无法查看OAuth2详细信息,请安排此方法返回isAuthenticated = false。
为NotAuthenticated编写定制处理程序
使用以下方式注册过滤器:
@Bean
public SecurityWebFilterChain springSecurityFilterChain(
ServerHttpSecurity http,
WebFilter proxyAuthFilter
) {
return http.addFilterAt(proxyAuthFilter, SecurityWebFiltersOrder.AUTHENTICATION); // Do configuration ...
}
过滤器是这样的:
public class ProxyAuthFilter implements WebFilter {
static private Logger LOGGER = LoggerFactory.getLogger(ProxyAuthFilter.class);
private final AuthZClientReactive authZClient;
public ProxyAuthFilter(AuthZClientReactive authZClient) {
this.authZClient = authZClient;
}
/**
* Process the Web request and (optionally) delegate to the next
* {@code WebFilter} through the given {@link WebFilterChain}.
*
* @param exchange the current server exchange
* @param chain provides a way to delegate to the next filter
* @return {@code Mono<Void>} to indicate when request processing is complete
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return ReactiveSecurityContextHolder.getContext()
.flatMap(this::transform)
.then(chain.filter(exchange));
}
private Mono<Authentication> transform(SecurityContext securityContext) {
Authentication authentication = securityContext.getAuthentication();
if (authentication.isAuthenticated()) {
return authenticate(authentication)
.map(a -> {
securityContext.setAuthentication(a);
return a;
});
} else {
LOGGER.info("ProxyFilter - not authenticated {}", authentication);
return Mono.just(authentication);
}
}
// Runs the chain, then returns a Mono with the exchange object which completes
// when the auth header is added to the request.
private Mono<Authentication> authenticate(Authentication authentication) {
LOGGER.info("ProxyFilter - Checking authorisation for {}", authentication);
Mono<AuthTokenInfo.Builder> authInfo = null;
// Catch if this filter runs twice
if (authentication instanceof ProxyAuthentication) {
LOGGER.info("ProxyAuthentication already found");
return Mono.just(authentication);
} else if (authentication instanceof UsernamePasswordAuthenticationToken) {
// If lookup is successful, returns instance of ProxyAuthentication
return getUsernamePasswordAuth((UsernamePasswordAuthenticationToken) authentication);
} else if (authentication instanceof OAuth2AuthenticationToken) {
// If lookup is successful, returns instance of ProxyAuthentication
return getOAuthAuth((OAuth2AuthenticationToken) authentication);
} else {
LOGGER.info("Unknown principal {}", authentication);
// Signals a failed authentication, can be picked up by error page
// to display bespoke information
return Mono.just(new ProxyAuthenticationNotAuthenticated(
authentication, ProxyAuthenticationNotAuthenticated.Reason.UnknownAuthenticationType)
);
}
}