You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenLiberty认证:如何向Principal添加角色信息

在OpenLiberty OIDC认证后增强Principal添加用户组方案

针对你遇到的OIDC认证后需从专有REST接口获取用户组并注入Principal以支持@RolesAllowed的场景,以下是OpenLiberty生态下的可行方案:

方案1:自定义UserRegistry(推荐,贴合OpenLiberty安全架构)

OpenLiberty的UserRegistry是安全体系核心组件之一,负责提供用户身份验证与角色/组信息查询能力。我们可以实现自定义UserRegistry,在OIDC认证完成后调用专有接口获取用户组:

步骤

  1. 实现com.ibm.websphere.security.UserRegistry接口,重点实现getGroupsForUser方法:
public class CustomOIDCUserRegistry extends UserRegistry {
    private String restGroupEndpoint;

    // 通过配置注入REST接口地址
    public void setRestGroupEndpoint(String restGroupEndpoint) {
        this.restGroupEndpoint = restGroupEndpoint;
    }

    @Override
    public List<String> getGroupsForUser(String userId) throws UserRegistryException {
        // 调用专有REST接口获取用户组列表
        try {
            // 此处实现HTTP调用逻辑,例如使用HttpClient
            HttpResponse response = HttpClient.newHttpClient().send(
                HttpRequest.newBuilder(URI.create(restGroupEndpoint + "?uid=" + userId))
                    .GET()
                    .build(),
                HttpResponse.BodyHandlers.ofString()
            );
            // 解析响应为组列表(示例为JSON格式)
            return Arrays.asList(new ObjectMapper().readValue(response.body(), String[].class));
        } catch (Exception e) {
            throw new UserRegistryException("Failed to fetch groups for user: " + userId, e);
        }
    }

    // 其他接口可根据需求实现,若仅需组查询可默认返回空或抛出UnsupportedOperationException
    @Override
    public String getRealm() throws UserRegistryException {
        return "OIDC-Custom-Realm";
    }

    @Override
    public User getUser(String userId) throws UserRegistryException {
        return new User(userId, userId, userId);
    }

    // 省略其他未实现方法...
}
  1. 在OpenLiberty配置中注册自定义UserRegistry并关联OIDC客户端:
<!-- 注册自定义用户注册表 -->
<userRegistry id="customOIDCRegistry" className="com.yourcompany.CustomOIDCUserRegistry">
    <property name="restGroupEndpoint" value="${REST_GROUP_ENDPOINT}"/>
</userRegistry>

<!-- 关联到OIDC客户端 -->
<openidConnectClient id="webapp_oidc_client"
                     clientId="${OIDC_CLIENT_ID}"
                     clientSecret="${OIDC_CLIENT_SECRET}"
                     discoveryEndpointUrl="${OIDC_DISCOVERY_URI}"
                     userIdentifier="uid"
                     signatureAlgorithm="RS256"
                     scope="openid"
                     responseType="code"
                     useNonce="true"
                     pkceCodeChallengeMethod="S256"
                     preferredJwsAlgorithm="RS256"
                     useSystemPropertiesForHttpClientConnections="true"
                     userRegistryRef="customOIDCRegistry"/>

方案2:JASPIC认证模块(标准Java EE扩展)

JASPIC(Jakarta Authentication)提供了标准化的认证扩展点,可在OIDC认证完成后增强Principal:

步骤

  1. 实现javax.security.auth.message.module.ServerAuthModule,在认证后阶段注入角色:
public class GroupAugmentAuthModule implements ServerAuthModule {
    private Class<?>[] supportedMessageTypes = new Class[]{HttpServletRequest.class, HttpServletResponse.class};

    @Override
    public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException {
        HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
        Principal user = request.getUserPrincipal();

        if (user != null && clientSubject.getPrincipals(AugmentedPrincipal.class).isEmpty()) {
            // 调用REST接口获取组
            List<String> groups = fetchUserGroups(user.getName());
            // 创建增强Principal并加入Subject
            AugmentedPrincipal augmentedPrincipal = new AugmentedPrincipal(user, groups);
            clientSubject.getPrincipals().add(augmentedPrincipal);
        }
        return AuthStatus.SUCCESS;
    }

    private List<String> fetchUserGroups(String userId) {
        // 实现REST调用逻辑
        return Collections.emptyList();
    }

    // 实现其他接口方法...
    @Override
    public Class<?>[] getSupportedMessageTypes() {
        return supportedMessageTypes;
    }

    // 省略其他未实现方法...

    public static class AugmentedPrincipal implements Principal {
        private final Principal delegate;
        private final List<String> roles;

        public AugmentedPrincipal(Principal delegate, List<String> roles) {
            this.delegate = delegate;
            this.roles = roles;
        }

        @Override
        public String getName() {
            return delegate.getName();
        }

        public List<String> getRoles() {
            return roles;
        }
    }
}
  1. 在OpenLiberty中配置JASPIC模块:
<authConfig provider="com.yourcompany.GroupAugmentAuthModule" layer="HttpServlet" appContext="/*"/>

方案3:Servlet Filter(快速实现,轻量场景)

对于简单场景,可通过Servlet Filter在请求流程中包装Principal,注入角色信息:

@WebFilter(urlPatterns = "/*")
public class GroupAugmentFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        Principal originalPrincipal = req.getUserPrincipal();

        if (originalPrincipal != null && !(originalPrincipal instanceof RoleAugmentedPrincipal)) {
            // 调用REST接口获取用户组
            List<String> userGroups = callGroupApi(originalPrincipal.getName());
            // 包装请求,替换Principal
            HttpServletRequest wrappedReq = new HttpServletRequestWrapper(req) {
                @Override
                public Principal getUserPrincipal() {
                    return new RoleAugmentedPrincipal(originalPrincipal, userGroups);
                }

                @Override
                public boolean isUserInRole(String role) {
                    return userGroups.contains(role);
                }
            };
            chain.doFilter(wrappedReq, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    private List<String> callGroupApi(String userId) {
        // 实现REST调用逻辑
        return Collections.emptyList();
    }

    private static class RoleAugmentedPrincipal implements Principal {
        private final Principal delegate;
        private final List<String> roles;

        public RoleAugmentedPrincipal(Principal delegate, List<String> roles) {
            this.delegate = delegate;
            this.roles = roles;
        }

        @Override
        public String getName() {
            return delegate.getName();
        }
    }
}

关键注意事项

  • 缓存优化:REST接口调用存在性能开销,建议添加本地缓存或使用OpenLiberty分布式缓存,避免重复调用。
  • 权限控制:调用专有REST接口时,可使用OIDC返回的Access Token作为身份凭证(若接口支持)。
  • 异常处理:需处理REST接口调用失败的场景,例如返回默认角色集合或抛出认证异常。

内容的提问来源于stack exchange,提问作者Mark Hunt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 04:47:34