我想根据领域角色给予访问权限。我正在使用Keycloak和Sping Boot 3(Spring Security 6)。
我尝试使用hasRole()/hasAnyRole/hasAuthority/hasAnyAuthority来获得所需的角色名称,字母大小写相同。
默认情况下,use-resource-role-mappings
为false(“如果为false,它将查看用户角色Map的领域级别”)。
我想在java配置文件中设置这个,因为我会发现添加每个控制器@PreAuthorize
注解非常耗时。
我一直得到403作为响应。我做错了什么?错误:
Retrieved SecurityContextImpl [Authentication=OAuth2AuthenticationToken [Principal=Name: [firstname.lastname], Granted Authorities: [[OIDC_USER, SCOPE_ProjectNameClientScope, SCOPE_email, SCOPE_openid, SCOPE_profile]],
User Attributes: [{at_hash=hash-hash-hash, sub=sub-sub-sub-sub-sub, email_verified=true, iss=https://keycloak.domain.ac.at:port/realms/ProjectNameRealm, groups=[/pn_administratoren],
Roles=[cafeteria, obw, ROLE_obw, courseplanning], typ=ID, preferred_username=firstname.lastname, given_name=firstname, nonce=nonce-y-nonce-nonce, sid=sid-sid-sid-sid-sid, aud=[po-ms], acr=1, persId=number, azp=po-ms, auth_time=2023-01-18T13:36:38Z, name=firstname lastname, exp=2023-01-18T13:41:38Z,
session_state=sss-sss-sss-ss-sss, family_name=lastname, iat=2023-01-18T13:36:38Z, email=firstname.lastname@domain.ac.at, jti=jti-jti-jti-jti-jti}],
Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=xxx.xxx.xx.xx, SessionId=sesId],
Granted Authorities=[OIDC_USER, SCOPE_ProjectNameClientScope, SCOPE_email, SCOPE_openid, SCOPE_profile]]]
下面是我的代码:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
@Configuration
@EnableWebSecurity
public class SpringSecurity2 {
public interface Jwt2AuthoritiesConverter extends Converter<Jwt, Collection<? extends GrantedAuthority>> {
}
@SuppressWarnings("unchecked")
@Bean
public Jwt2AuthoritiesConverter authoritiesConverter() {
// This is a converter for roles as embedded in the JWT by a Keycloak server
// Roles are taken from both realm_access.roles & resource_access.{client}.roles
return jwt -> {
final var realmAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("realm_access", Map.of());
final var realmRoles = (Collection<String>) realmAccess.getOrDefault("roles", List.of());
final var resourceAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("resource_access", Map.of());
// We assume here you have "spring-addons-confidential" and "spring-addons-public" clients configured with "client roles" mapper in Keycloak
final var confidentialClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("spring-addons-confidential", Map.of());
final var confidentialClientRoles = (Collection<String>) confidentialClientAccess.getOrDefault("roles", List.of());
final var publicClientAccess = (Map<String, Object>) resourceAccess.getOrDefault("spring-addons-public", Map.of());
final var publicClientRoles = (Collection<String>) publicClientAccess.getOrDefault("roles", List.of());
return Stream.concat(realmRoles.stream(), Stream.concat(confidentialClientRoles.stream(), publicClientRoles.stream()))
.map(SimpleGrantedAuthority::new).toList();
};
}
public interface Jwt2AuthenticationConverter extends Converter<Jwt, AbstractAuthenticationToken> {
}
@Bean
public Jwt2AuthenticationConverter authenticationConverter(Jwt2AuthoritiesConverter authoritiesConverter) {
return jwt -> new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt));
}
@Bean
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, Converter<Jwt, AbstractAuthenticationToken> authenticationConverter, KeycloakLogoutHandler keycloakLogoutHandler) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName("_csrf");
CsrfTokenRequestHandler requestHandler = delegate::handle;
http.authorizeHttpRequests(auth -> {
auth.requestMatchers("/firstpath/**", "/secondpath/**").permitAll();
auth.requestMatchers("/thirdpath/**").hasAnyRole("obw");
auth.anyRequest().authenticated();
});
http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(authenticationConverter);
http.oauth2Login()
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler)
.logoutSuccessUrl("/");
http.csrf(csrf -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler));
return http.build();
}
}
登录用户的角色
ROLE_obw也是如此...
这是访问令牌的一部分:
2条答案
按热度按时间vawmfj5a1#
默认情况下,Spring安全性只提供基于作用域的Map,用于处理通过JWT令牌的访问。对于授权/角色,需要自定义Map解决方案来从JWT令牌中提取并放入安全上下文。
有一篇很好的文章解释了这一点。
Map Authorities from JWT
i7uq4tfw2#
登录(和注销)由OAuth2客户端处理,而不是由资源服务器(REST API)处理。对受资源服务器保护的资源的请求应该具有访问令牌。
从资源服务器安全过滤器链中删除登录和注销配置(您也可以使其无会话并禁用CSRF),并在
Authorization
报头中发送带有Bearer
访问令牌的请求。如果您的应用还提供服务器端呈现的UI,请为客户端配置定义第二个
SecurityFilterChain
bean(启用登录、注销、会话和CSRF保护)。我在回答另一个问题时的详细内容如下:将KeycloakSpring适配器与 Spring Boot 3配合使用