Keycloak保护REST API遇403错误(!role)的问题咨询
我已经用Keycloak给REST API做了安全防护,完成认证后访问API却碰到403错误,提示:
403 error. Unable to access rest api. Reason !role.
我原本以为用*代表任意角色,配置代码如下:
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); context.setSecurityHandler(securityHandler); securityHandler.addRole("*"); ConstraintMapping constraintMapping = new ConstraintMapping(); constraintMapping.setPathSpec("/*"); Constraint constraint = new Constraint(); constraint.setAuthenticate(true); constraint.setRoles(new String[]{"*"});
想问问是我用*代表任意角色的假设错了,还是这个错误有别的含义?
JettyKeycloakAuthenticator的日志内容如下:
2018-05-24 12:55:52,253 [DEBUG] [ers.PreAuthActionsHandler(handleRequest )] - adminRequest http://localhost:7100/api/v1/design/test
2018-05-24 12:55:52,254 [DEBUG] [TokenRequestAuthenticator(thenticateToken)] - Verifying access_token
2018-05-24 12:55:52,255 [DEBUG] [TokenRequestAuthenticator(thenticateToken)] - successful authorized
2018-05-24 12:55:52,255 [DEBUG] [JettyRequestAuthenticator(rAuthentication)] - Completing bearer authentication. Bearer roles: [uma_authorization]
2018-05-24 12:55:52,255 [DEBUG] [ters.RequestAuthenticator(eAuthentication)] - User '8f9381df-2f7e-4ff8-9ef5-2123b03db3c9' invoking 'http://localhost:7100/api/v1/design/test' on client 'my_server'
2018-05-24 12:55:52,255 [DEBUG] [ters.RequestAuthenticator(authenticate )] - Bearer AUTHENTICATED
2018-05-24 12:55:52,255 [DEBUG] [thenticatedActionsHandler(handledRequest )] - AuthenticatedActionsValve.invoke http://localhost:7100/api/v1/design/test
首先,你的假设确实错了——Jetty的Constraint里用"*"并不代表任意角色,它会把*当成一个具体的角色名称来校验。从日志里能看到,你的Bearer token里只有uma_authorization这个角色,而Jetty在检查时会去找名为*的角色,自然匹配失败,返回403。
那要实现“任意已认证用户都能访问”的需求,有两种靠谱方式:
方式一:只要求认证,不指定角色
把constraint.setRoles(new String[]{"*"});这行删掉,或者设置为空数组。因为当你开启constraint.setAuthenticate(true);时,只要用户完成认证,即使没有指定任何角色,也能通过校验。修改后的代码片段:
Constraint constraint = new Constraint(); constraint.setAuthenticate(true); // 移除setRoles这行,或者设置为new String[]{}
方式二:动态添加用户的实际角色
如果你的场景需要基于角色控制,但想允许用户拥有的所有角色都能访问,可以从Keycloak的认证信息里提取用户的角色,再添加到securityHandler中。比如:
// 从请求中获取Keycloak认证信息 KeycloakPrincipal<?> principal = (KeycloakPrincipal<?>) request.getUserPrincipal(); AccessToken accessToken = principal.getKeycloakSecurityContext().getToken(); Set<String> roles = accessToken.getRealmAccess().getRoles(); // 将用户所有角色添加到securityHandler for (String role : roles) { securityHandler.addRole(role); } // 约束中设置这些角色 constraint.setRoles(roles.toArray(new String[0]));
另外,从日志里看到你的token只有uma_authorization角色,如果你本来期望有其他角色,还得检查下Keycloak的配置:
- 确认客户端的**“Full Scope Allowed”**是否开启,或者是否手动添加了需要的角色到客户端范围
- 检查用户是否被正确分配了对应的Realm角色或客户端角色
最后提一句,Jetty的ConstraintSecurityHandler角色匹配是严格的字符串匹配,别想用通配符偷懒哦~
内容的提问来源于stack exchange,提问作者tryingToLearn

