基于Keycloak的权限控制

Keycloak授权介绍

Keycloak提供了一套基于策略的权限控制,主要的控制对象是被保护的资源,通常是一个或者多个URIs。

Keycloak的权限控制通过所谓的PEP(Policy Enforcement Point)即:策略执行点来操作。

Keycloak授权系统的实体对象

如上图所以,把所有授权实体分成了三个大的部分。

最上面的部分,我称之为资源定义,负责设置哪些资源(resource)的那些范围(scope)要被权限控制。资源通常是一个或者一组URI,比如/protected/*,而范围(scope)是一个很灵活的概念,既可以是对资源的CRUD,也可以是具有某一种特质的一组对象。通常,我们可以用scope来标识对资源的CRUD,比如:urn:demo:protected:view,urn:demo:protected:delete等等。

下面的部分,我称之为执行策略,负责设置执行权限的方式。比如,可以根据用户是否属于某个角色来检查权限,或者可以根据时间来检查权限等等,这里我列出了一些常用的执行策略,其实每个策略对应的一个权限模型:

  • 基于角色的权限控制
  • 基于用户的权限控制
  • 基于组的权限控制(组可以有层级关系)
  • 基于时间的权限控制
  • 基于正则表达式的权限控制

中间的部分,我称之为权限关联,负责把执行策略与资源进行绑定。

Keycloak关于授权的设置

在Keycloak中,授权相关的配制,绝大部分都在具体的客户端设置里面。并且必须开启授权开关。

集成Keycloak授权功能

在Keycloak上对资源的权限进行配置以后,就可以通过编程的方式,来利用Keycloak来保护我们的微服务(也称资源服务器)了。

keycloak权限设置

既可以通过keycloak的admin console来设置权限,也可以通过Java客户端来设置权限。无论哪种方式,大致的过程如下:

  • 定义resource和scope
  • 定义policy
  • 根据需要定义角色、组等等
  • 定义permission来关联resource(scope)与policy

通过admin console配置

主要就是理解了资源、scope、policy和permission的概念后,在admin console中依次进行配置即可。

通过Java客户端配置

1)引入依赖

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-authz-client</artifactId>
     <version>${keycloak.version}</version>
</dependency>

2)编写代码

// 初始化授权客户端对象
AuthzClient authzClient = AuthzClient.create();

// 获取pat对象
ProtectionResource protection = authzClient.protection();

// 创建资源(resource)和scope,这里创建的资源名称为test-res,scope为create-scope,
// URI为/admin/*,类型为test:api
ResourceRepresentation res = new ResourceRepresentation("test-res",
        Set.of(new ScopeRepresentation("create-scope")),
        Set.of("/admin/*"),
        "test:api");
res.setOwnerManagedAccess(true);

ProtectedResource resClient = protection.resource();
ResourceRepresentation existingRes = resClient.findByName(res.getName());
if (existingRes != null) {
    resClient.delete(existingRes.getId());
}

res = resClient.create(res);
String resId = res.getId();

// 为上述资源创建权限
// 注意:这里创建的权限,在admin console上不可见
UmaPermissionRepresentation umaPerm = new UmaPermissionRepresentation();
umaPerm.setRoles(Set.of("user_premium"));
umaPerm.setName("Only premium user can create resources");
umaPerm.setDescription("Allow access to premium users");
umaPerm.setScopes(Set.of("create-scope"));
UmaPermissionRepresentation createdUmaPerm = protection.policy(resId).create(umaPerm); 

 

建议

  • 建议通过admin console来管理资源/scope/policy/permission
  • 可以使用Java来管理资源,但是不建议使用Java来管理policy和permission

使用keycloak设置的权限保护资源

在keycloak上配置了资源、策略、权限以后,可以在资源服务器上来通过PEP进行权限控制。以下是步骤:

配置keycloak的enforcer

新增一个json文件,放在resources目录下,内容大致如下:

{
  "realm": "quickstart",
  "auth-server-url": "http://localhost:8180",
  "resource": "authz-servlet",
  "credentials": {
    "secret": "secret"
  },
  "http-method-as-scope": false
}

增加一个Filter来解析上述配置,并拦截用户请求

在Spring Security的FilterChain中,增加ServletPolicyEnforcerFilter来真正执行权限检查。

@Configuration
@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((authorize) -> authorize
                        .anyRequest().authenticated()
                )
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .addFilterAfter(createPolicyEnforcerFilter(), BearerTokenAuthenticationFilter.class);
        return http.build();
    }

    private ServletPolicyEnforcerFilter createPolicyEnforcerFilter() {
        return new ServletPolicyEnforcerFilter(new ConfigurationResolver() {
            @Override
            public PolicyEnforcerConfig resolve(HttpRequest request) {
                try {
                    return JsonSerialization.readValue(getClass().getResourceAsStream("/policy-enforcer.json"), PolicyEnforcerConfig.class);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    @Bean
    JwtDecoder jwtDecoder(OAuth2ResourceServerProperties properties) {
        return NimbusJwtDecoder.withJwkSetUri(properties.getJwt().getJwkSetUri()).build();
    }
}

Bonus:利用AuthorizationContext判断权限

除了使用ServletPolicyEnforcerFilter来进行权限检查以外,我们还可以利用AuthorizationContext来手动判断当前用户是否对某项资源有访问权限。示例代码如下:

@GetMapping("/protected/premium")
public String premium(@AuthenticationPrincipal Jwt jwt, HttpServletRequest request) {

	// 获取当前的AuthorizationContext对象
	AuthorizationContext context = (AuthorizationContext) request.getAttribute(AuthorizationContext.class.getName());
	// 调用context上的方法进行手动判断
    if (context.hasPermission("Premium Resource", "GET")) {
		// do something intersting...
	}

	return String.format("Hello, %s!", jwt.getClaimAsString("preferred_username"));
}
这种代码,可以用来构建动态菜单。比如,在用户登录以后,通过这种权限判断,来觉得哪些菜单显示/隐藏。

Bonus:利用AuthzClient来操纵资源

通过AuthorizationContext,我们可以获取到AuthzClient,通过它我们就可以创建资源或者进行个别的权限检查...

// 将AuthorizationContext强转成ClientAuthorizationContext
ClientAuthorizationContext clientContext = (ClientAuthorizationContext) context;

// 从clientContext中获取AuthzClient实例,然后调用它上面的方法
AuthzClient client = clientContext.getClient();
ResourceRepresentation resource = client.protection().resource().findByName("Premium Resource");
System.out.println(resource);

Bonus:从JS调用keycloak的API

在JS中,可以通过keycloak提供的keycloak-authz.js的API来进行权限判断。

首先,需要引入keycloak-authz.js文件

<script src="http://.../js/keycloak-authz.js"></script>

然后调用其上的API在JS端进行权限检查和控制。比如:

// prepare a authorization request with the permission ticket
const authorizationRequest = {};
authorizationRequest.ticket = ticket;

// send the authorization request, if successful retry the request
Identity.authorization.authorize(authorizationRequest).then(function (rpt) {
    // onGrant
}, function () {
    // onDeny
}, function () {
    // onError
});

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot 集成 Keycloak 可以实现基于角色的权限认证。下面是一个简单的步骤: 1. 在 pom.xml 文件中添加 Keycloak 依赖: ```xml <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-spring-boot-starter</artifactId> <version>${keycloak.version}</version> </dependency> ``` 2. 在 application.properties 文件中添加 Keycloak 配置: ```properties keycloak.realm=your-realm keycloak.auth-server-url=http://localhost:8080/auth keycloak.ssl-required=external keycloak.resource=your-client-id keycloak.credentials.secret=your-client-secret keycloak.use-resource-role-mappings=true ``` 3. 创建一个 Keycloak 配置类: ```java @Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider(); keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper()); auth.authenticationProvider(keycloakAuthenticationProvider); } @Bean public KeycloakSpringBootConfigResolver keycloakConfigResolver() { return new KeycloakSpringBootConfigResolver(); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .anyRequest().permitAll(); } } ``` 4. 创建一个 Controller: ```java @RestController public class HomeController { @GetMapping("/") public String home() { return "Welcome!"; } @GetMapping("/admin") public String admin() { return "Welcome, admin!"; } } ``` 现在,当用户访问 /admin 路径时,只有拥有 admin 角色的用户才能访问该路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值