前面的文章讲过Spring Boot应用集成Keycloak实现统一身份验证、权限控制的方法,其中权限控制的部分直接使用Role进行判断,这种方式对于简单的系统来说也够用。但是对于复杂的需要集成细粒度权限控制的系统,显然只是单纯的依靠Role直接控制的方式是不合适的,并且应用本身与Role直接耦合的方式也很不灵活。本文讲述Spring Boot的Web应用如何集成Keycloak实现细粒度权限控制。
Keycloak核心权限概念综述
Keycloak支持细粒度权限控制策略,可以组合使用如下不同的访问控制机制:
- Attribute-based access control (ABAC)
- Role-based access control (RBAC)
- User-based access control (UBAC)
- Context-based access control (CBAC)
- Rule-based access control
- Time-based access control
Keycloak授权处理流程
如果要使用Keycloak的细粒度权限控制,主要有3个流程需要了解:
资源管理
资源管理主要用来定义资源服务器的哪些对象需要被保护

其中资源服务、资源、范围(Scope)关系如下:

权限和策略管理(Permission and Policy Management)
策略是来定义满足哪些条件才可以访问资源,不过策略本身并不与资源关联。要想策略实际作用在相应的资源上,就需要配置权限,将要保护的资源及策略进行关联。

策略执行
策略执行需要在资源服务内集成一个 Policy Enforcement Point (PEP,资源执行点),以便与Keycloak服务进行通信获取相关的权限信息等,从而决定哪些资源可以被访问。

Keycloak细粒度权限控制后台配置
要想启用Keycloak的细粒度权限控制,需要先创建相应的客户端,并启用授权服务。然后根据上面的授权处理流程,进行资源管理、权限和策略管理的配置。本文模拟如下场景来进行配置,以便更好的理解配置内容。
场景如下:
- 需要被保护的应用有
/admin
、/customer/view
、/customer/delete
三个主要资源 - 拥有
ROLE_ADMIN
角色的用户可以访问所有资源 - 拥有
ROLE_CUSTOMER
角色的用户仅可以访问/customer/view
,其他资源不能访问
接下来便看下针对如上模拟的场景,在Keycloak后台具体要如何配置。
创建角色及用户
-
创建3个角色,其中
ROLE_USER
给所有用户都进行绑定
-
创建2个用户并绑定角色
创建客户端并启用授权服务
-
新建客户端
-
设置
confidential
访问类型,并启用授权
配置资源、策略及权限
在客户端的 Authorization
Tab下,有很多子的选项卡,可以对资源、策略及权限进行配置。
Settings选项卡配置
Settings选项卡下的是关于授权的一些全局默认配置

这里说明下Settings下面的2个重要配置
-
Policy Enforcement Mode:指定授权服务器接受到请求时策略如何执行
- Enforcing:当资源没有配置关联的策略时,请求默认被拒绝访问,这也是默认的选项
- Permissive:当资源没有配置关联的策略时,请求允许访问
- Disabled:禁用所有资源的所有访问策略
-
Decision Strategy:表示权限最终是如何计算的策略,以决定相应的资源是否能获得授权
- Affirmative :至少一个权限计算做出正向决定
- Unanimous:所有的权限计算都要做出正向决定
Resources选项卡配置
Resoucres选项卡主要是用来配置需要保护的资源。本示例中配置如下几个资源,注意下Scopes的设置




Authorization Scopes选项卡配置

Policies选项卡配置
Policies下主要配置哪些策略,配置的策略用来在权限设置中与资源进行关联,Keycloak本身支持很多策略,详细的策略说明可参考官方文档,本示例中使用最常使用的基于角色(Role Based)的策略



Permissions选项卡配置
Permission下就是用来配置资源、策略如何进行关联,并且在有多个策略关联时采用何种策略(Decision Strategy)最终决定资源是否能被授权,Decision Strategy的配置项与上文Settings下的意义相同,在此不再赘述




Spring Boot项目集成Keycloak代码配置示例
application.yml Keycloak配置
keycloak: realm: demo auth-server-url: http://127.0.0.1:8080/auth resource: spring-boot-authz-keycloak-web ssl-required: external credentials: secret: dede7fd6-2817-491c-b7e5-27f65bbb5fc7 use-resource-role-mappings: false bearer-only: false autodetect-bearer-only: false security-constraints: - authRoles: - ROLE_USER securityCollections: - name: all patterns: - /* policy-enforcer-config: on-deny-redirect-to: /accessDenied 复制代码
配置文件说明可参考之前的文章,这里主要说下,有 policy-enforcer-config
配置项,表明当前应用启用细粒度权限控制, on-deny-redirect-to
表示当访问被拒绝时跳转的地址
Controller示例代码
@RequestMapping(value = "/customer/view", method = {RequestMethod.GET}) public String customerView(Model model, HttpServletRequest request) { KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); model.addAttribute("authz", keycloak.getAuthorizationContext()); return "customer/view"; } @RequestMapping(value = "/customer/delete", method = {RequestMethod.GET, RequestMethod.POST}) public String customerDelete(Model model, HttpServletRequest request) { KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); model.addAttribute("authz", keycloak.getAuthorizationContext()); return "customer/delete"; } @RequestMapping(value = "/admin", method = {RequestMethod.GET, RequestMethod.POST}) public String admin(Model model, HttpServletRequest request) { KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); model.addAttribute("authz", keycloak.getAuthorizationContext()); return "admin/view"; } @RequestMapping(value = "/admin/delete", method = {RequestMethod.GET, RequestMethod.POST}) public String adminDelete(Model model, HttpServletRequest request) { KeycloakSecurityContext keycloak = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName()); model.addAttribute("authz", keycloak.getAuthorizationContext()); return "admin/delete"; } @RequestMapping(value = "/accessDenied", method = {RequestMethod.GET, RequestMethod.POST}) public String accessDenied() { return "access_denied"; } 复制代码
示例Controller主要是将Keycloak的授权上下文传给model,并指定相应的视图渲染
customer/view.ftl页面示例代码
<h1>Customer View</h1> <div> <#if authz.hasScopePermission("delete")> <a href="/customer/delete">delete</a> </#if> </div> 复制代码
页面使用freemarker,都比较简单,当然也可以使用其他视图引擎,此处仅贴出这一个页面的示例代码,只有拥有 delete
Scope的权限才能够看到delete链接
项目效果演示
customer用户访问效果
访问/customer/view页面,看不到delete链接

访问/custome/delete、/admin页面,直接跳转/accessDenied页面

admin用户访问效果
admin用户访问/customer/view页面,可以看到delete链接

admin用户点击delete链接或者直接访问/customer/delete,都可以正常显示页面

admin用户可以正常访问/admin相关页面

原文
https://juejin.im/post/5ef764fd6fb9a07e8223eae6
本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Spring Boot Web应用集成Keycloak进阶之细粒度权限控制