Spring GateWay OAuth 2 ResourceServer構成Server HttpSecurityでhasRoleが無効な問題
8263 ワード
文書ディレクトリ問題 ソリューション JwtGrantedAuthoritiesConverterソース カスタムReactiveAuthenticationManager 完全構成コード に質問
gatewayがOAuth 2 ResourceServerとして機能すると、hasRole構成が無効になるという問題が発生します.
なぜなら、
簡単に言えば、jwtのClaimのauthoritiesの値をここに入れる必要があるという個人的な質問ですが、公式には
ソリューション
JwtGrantedAuthoritiesConverterソースコード
ソースコードを見ると、ソースコードは長くありません.
実は
また、ソースコードに基づいて、この変換器
これで簡単にやりやすい.
私たちは自分でコンバータを書く必要もありません
カスタムReactiveAuthenticationManager
さらに、ServerHttpSecurityを設定するときに、前の文を追加します.
完全な構成コード
注意!hasroleの値はjwt負荷の値と一致しなければならない.オンラインdebugjwt
全編のコードを見てほとんど説明がないのではなく、説明がはっきりしていないので、ソースコードと注釈を見る時間を与えてください.とても分かりやすいです.
この方法は純粋に私自身がでたらめに模索したもので、皆さんがその中の間違いを多く指導し、指摘し、子弟を誤解しないようにしてほしい.
gatewayがOAuth 2 ResourceServerとして機能すると、hasRole構成が無効になるという問題が発生します.
なぜなら、
ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec
のデフォルトのReactiveAuthenticationManager
がjwtのauthoritiesの負荷部分をAuthenticationの権限としていないからです.簡単に言えば、jwtのClaimのauthoritiesの値をここに入れる必要があるという個人的な質問ですが、公式には
scope``scp
という2つのフィールドを権限として使うだけで十分だと考えられていますgithub issueソリューション
ReactiveAuthenticationManager
パーミッションマネージャのデフォルトのパーミッションマネージャを再定義します.jwtGrantedAuthoritiesConverter
をデフォルトのコンバータとして使用します.このコンバータはデフォルトで「scope」、「scp」の2つだけをパーミッションとして読み込みます.JwtGrantedAuthoritiesConverterソースコード
ソースコードを見ると、ソースコードは長くありません.
convert
メソッドは変換器の呼び出しエントリとして理解でき,このメソッドから見ると/**
* Extracts the {@link GrantedAuthority}s from scope attributes typically found in a
* {@link Jwt}.
*
* @author Eric Deandrea
* @since 5.2
*/
public final class JwtGrantedAuthoritiesConverter implements Converter> {
private static final String DEFAULT_AUTHORITY_PREFIX = "SCOPE_";
private static final Collection WELL_KNOWN_AUTHORITIES_CLAIM_NAMES =
Arrays.asList("scope", "scp");
private String authorityPrefix = DEFAULT_AUTHORITY_PREFIX;
private String authoritiesClaimName;
/**
* Extract {@link GrantedAuthority}s from the given {@link Jwt}.
*
* @param jwt The {@link Jwt} token
* @return The {@link GrantedAuthority authorities} read from the token scopes
*/
@Override
public Collection convert(Jwt jwt) {
Collection grantedAuthorities = new ArrayList<>();
for (String authority : getAuthorities(jwt)) {
grantedAuthorities.add(new SimpleGrantedAuthority(this.authorityPrefix + authority));
}
return grantedAuthorities;
}
/**
* Sets the prefix to use for {@link GrantedAuthority authorities} mapped by this converter.
* Defaults to {@link JwtGrantedAuthoritiesConverter#DEFAULT_AUTHORITY_PREFIX}.
*
* @param authorityPrefix The authority prefix
* @since 5.2
*/
public void setAuthorityPrefix(String authorityPrefix) {
Assert.hasText(authorityPrefix, "authorityPrefix cannot be empty");
this.authorityPrefix = authorityPrefix;
}
/**
* Sets the name of token claim to use for mapping {@link GrantedAuthority authorities} by this converter.
* Defaults to {@link JwtGrantedAuthoritiesConverter#WELL_KNOWN_AUTHORITIES_CLAIM_NAMES}.
*
* @param authoritiesClaimName The token claim name to map authorities
* @since 5.2
*/
public void setAuthoritiesClaimName(String authoritiesClaimName) {
Assert.hasText(authoritiesClaimName, "authoritiesClaimName cannot be empty");
this.authoritiesClaimName = authoritiesClaimName;
}
private String getAuthoritiesClaimName(Jwt jwt) {
if (this.authoritiesClaimName != null) {
return this.authoritiesClaimName;
}
for (String claimName : WELL_KNOWN_AUTHORITIES_CLAIM_NAMES) {
if (jwt.containsClaim(claimName)) {
return claimName;
}
}
return null;
}
private Collection getAuthorities(Jwt jwt) {
String claimName = getAuthoritiesClaimName(jwt);
if (claimName == null) {
return Collections.emptyList();
}
Object authorities = jwt.getClaim(claimName);
if (authorities instanceof String) {
if (StringUtils.hasText((String) authorities)) {
return Arrays.asList(((String) authorities).split(" "));
} else {
return Collections.emptyList();
}
} else if (authorities instanceof Collection) {
return (Collection) authorities;
}
return Collections.emptyList();
}
}
実は
getAuthorities()
という方法が核心です.コードを見るのは説明を見るより速く理解できます.また、ソースコードに基づいて、この変換器
setAuthoritiesClaimName
の方法はAuthoritiesClaimName
をカスタマイズできることが分かった.注記は、マッピング権限(Authorities)token負荷(token claim)を設定する名前を明確に示しています.これで簡単にやりやすい.
私たちは自分でコンバータを書く必要もありません
カスタムReactiveAuthenticationManager
......
private final
OAuth2ResourceServerProperties.Jwt Properties;
......
ReactiveAuthenticationManager getAuthenticationManager() {
NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = new NimbusReactiveJwtDecoder(Properties.getJwkSetUri());
JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager = new JwtReactiveAuthenticationManager(nimbusReactiveJwtDecoder);
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
ReactiveJwtAuthenticationConverterAdapter reactiveJwtAuthenticationConverterAdapter = new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
jwtReactiveAuthenticationManager.setJwtAuthenticationConverter(reactiveJwtAuthenticationConverterAdapter);
return jwtReactiveAuthenticationManager;
}
さらに、ServerHttpSecurityを設定するときに、前の文を追加します.
ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec jwtSpec = http.oauth2ResourceServer().jwt();
jwtSpec.authenticationManager(getAuthenticationManager());
完全な構成コード
/**
* Class: ResourceServerConfigurer
*
* @author GodDai
* @version 1.0.0
* @since 2020/2/28 11:52
*/
@EnableWebFluxSecurity
public class ResourceServerConfigurer {
private final
OAuth2ResourceServerProperties.Jwt Properties;
@Autowired
public ResourceServerConfigurer(OAuth2ResourceServerProperties Properties) {
this.Properties = Properties.getJwt();
}
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
// options
.pathMatchers(HttpMethod.OPTIONS).permitAll()
// path
// ! hasrole jwt
.pathMatchers("/api/admin/**").hasRole("ROLE_ADMIN")
//
.pathMatchers("/**").permitAll()
.anyExchange().authenticated()
.and().cors().disable()
.csrf().disable();
ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec jwtSpec = http.oauth2ResourceServer().jwt();
jwtSpec.authenticationManager(getAuthenticationManager());
SecurityWebFilterChain chain = http.build();
return chain;
}
ReactiveAuthenticationManager getAuthenticationManager() {
NimbusReactiveJwtDecoder nimbusReactiveJwtDecoder = new NimbusReactiveJwtDecoder(Properties.getJwkSetUri());
JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager = new JwtReactiveAuthenticationManager(nimbusReactiveJwtDecoder);
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
ReactiveJwtAuthenticationConverterAdapter reactiveJwtAuthenticationConverterAdapter = new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
jwtReactiveAuthenticationManager.setJwtAuthenticationConverter(reactiveJwtAuthenticationConverterAdapter);
return jwtReactiveAuthenticationManager;
}
}
注意!hasroleの値はjwt負荷の値と一致しなければならない.オンラインdebugjwt
全編のコードを見てほとんど説明がないのではなく、説明がはっきりしていないので、ソースコードと注釈を見る時間を与えてください.とても分かりやすいです.
この方法は純粋に私自身がでたらめに模索したもので、皆さんがその中の間違いを多く指導し、指摘し、子弟を誤解しないようにしてほしい.