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

基于Pac4j+Keycloak登录后获取用户信息及Shiro配置问题

基于Pac4j+Keycloak+Shiro的登录后用户信息处理方案

1. 核心实现逻辑

需要通过自定义Shiro Realm扩展Pac4jRealm,在认证流程中直接从Keycloak返回的用户Profile中提取信息,同时完成用户映射、会话存储、日志记录、权限配置等业务逻辑。必须修改shiro.ini,替换默认的Pac4jRealm为自定义实现类。

2. 自定义Keycloak Realm类

继承Pac4jRealm,重写认证方法,在基础认证完成后提取Keycloak用户信息并执行业务逻辑:

import org.pac4j.core.profile.CommonProfile;
import org.pac4j.shiro.realm.Pac4jRealm;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.pac4j.core.profile.UserProfile;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.SecurityUtils;
import java.util.List;
import java.util.Set;

public class CustomKeycloakRealm extends Pac4jRealm {
    // 注入数据库操作类(根据项目实际依赖方式调整)
    private UserMapper userMapper;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        // 完成Pac4j与Keycloak的基础认证流程
        AuthenticationInfo authInfo = super.doGetAuthenticationInfo(token);
        
        // 获取Keycloak返回的用户Profile集合
        List<UserProfile> profiles = getProfiles(token);
        if (profiles.isEmpty()) {
            return authInfo;
        }
        
        // 转换为通用Profile,提取邮箱(KeycloakProfile继承自CommonProfile)
        CommonProfile keycloakProfile = (CommonProfile) profiles.get(0);
        String userEmail = keycloakProfile.getEmail();
        
        // 数据库查询:通过邮箱映射应用内用户
        AppUser appUser = userMapper.findUserByEmail(userEmail);
        
        // 1. 存储用户信息到Shiro会话
        SecurityUtils.getSubject().getSession().setAttribute("currentAppUser", appUser);
        
        // 2. 记录登录日志
        UserLog.recordLogin(appUser.getUserId(), userEmail);
        
        // 3. 设置用户语言到会话
        SecurityUtils.getSubject().getSession().setAttribute("userLocale", appUser.getLanguage());
        
        // 4. 配置Shiro权限:添加应用内权限集
        Set<String> userPermissions = appUser.getPermissions();
        SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
        authzInfo.setStringPermissions(userPermissions);
        ((SimpleAuthenticationInfo) authInfo).setAuthorizationInfo(authzInfo);
        
        return authInfo;
    }

    // 注入数据库Mapper的Setter方法(非Spring环境需手动初始化)
    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
}

3. 修改shiro.ini配置

替换默认Realm为自定义实现,并配置Pac4j过滤器:

# 自定义Realm配置
customKeycloakRealm = com.yourcompany.app.shiro.CustomKeycloakRealm
# 注入数据库操作类
customKeycloakRealm.userMapper = com.yourcompany.app.mapper.UserMapper

# 绑定Realm到SecurityManager
securityManager.realms = $customKeycloakRealm

# Pac4j过滤器配置,关联已配置好KeycloakClient的Pac4j Config
pac4jFilter = org.pac4j.shiro.filter.Pac4jFilter
pac4jFilter.configFactory = com.yourcompany.app.config.KeycloakConfigFactory

# 过滤链规则:指定需要认证的路径
[urls]
/auth/login = pac4jFilter
/** = authc

4. 关键注意事项

  • 确保Keycloak客户端已配置返回email属性:在Keycloak控制台的客户端「客户端范围」中,开启email属性的映射并添加到访问令牌
  • 若项目基于Spring,可通过@Component注解让Spring管理CustomKeycloakRealm,无需在shiro.ini中手动注入依赖
  • AppUser类需实现Serializable接口,才能正常存入Shiro会话
  • 若不需要在Realm中处理所有业务,也可单独实现Pac4j的AuthenticationSuccessHandler,但通过Realm集成更贴合Shiro的认证授权流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:05:26