告别shiro-cas单点登录集成库,这款简单且强壮的Java Web安全引擎pac4j你值得拥有

简介: 告别shiro-cas单点登录集成库,这款简单且强壮的Java Web安全引擎pac4j你值得拥有

pac4j官网地址传送门。


pac4j引擎全称为powerful authentication client for java,这是笔者根据其官网介绍推测的全称,不一定正确,姑且这样叫着。


一、缘何遇到该引擎


笔者在集成CAS单点登录服务时使用springboot+shiro搭配shiro-cas库,但是总是遇到非法令牌的问题即invalid_ticket,找了各种解释。


1.CAS服务器令牌失效时间短的问题

笔者更改配置文件,尝试多次,貌似不起作用,官方说的这是默认且唯一的配置;

2.缺少其它依赖库

引入后也不得行;

3.客户端应用路径问题

Url缺少“/”


总之成功的都是相似的,不成功的原因千奇百怪。


更要命的是说,该库在退出的时候也有bug,折腾了许久,未果。


就在GitHub上搜springboot shiro cas,就出来了使用pac4j引擎的项目,而且是一个很简单的纯测试项目。项目地址传送门

1666264617425.jpg

下载跑起来测试一下,很舒服,直接成功。 😃

1666264725838.jpg


二、引擎能力


先来看一下这个安全引擎能够支持的框架,如下图,几乎包揽市面上的所有的框架,当然Shiro和Spring Security也在其中。在对接的时候引擎抽象了共同点使得工作变得简单。

1666264799148.jpg

支持的认证协议有:


OAuth (Facebook, Twitter, Google…) - SAML - CAS - OpenID Connect - HTTP - Google App Engine

LDAP - SQL - JWT - MongoDB - CouchDB - IP address - Kerberos (SPNEGO) - REST API


支持的授权类型有:


Roles/permissions - Anonymous/remember-me/(fully) authenticated - CORS - CSRF - HTTP Security headers


三、引擎特征


  1. 简单
  2. 高效
  3. 强壮


四、十大核心组件


序号 组件英文名称 组件中文名称 功能描述
1 client 客户端 代表一个认证流程,执行登录逻辑并返回用户信息;UI认证的客户端称为间接客户端(indirect client),web服务认证的客户端称为直接客户端
2 authenticator 认证器 用于HTTP客户端认证身份, ProfileService的子组件,ProfileService不仅验证用户身份,还进行用户信息的创建、更新和删除
3 authorizer 授权器 基于网页上下文信息和用户信息进行权限验证
4 matcher 匹配器 定义安全性是否必须应用于安全过滤器
5 config 配置器 通过客户端、授权器和匹配器定义安全配置
6 user profile 用户身份 经过身份验证的用户的配置文件,具有标识符、属性、角色、权限、“记住我”性质和链接标识符
7 web context 用户身份 pac4j实现的 HTTP 请求和响应以及关联表示会话的实现SessionStore的抽象
8 security filter 安全过滤器 根据客户端和授权器的配置,通过检查用户是否经过身份验证以及授权是否有效来保护请求访问的 URL,如果用户未通过身份验证,则对直接客户端执行身份验证或为间接客户端启动登录过程
9 callback endpoint 回调点 表示间接客户端登录流程的结束
10 logout endpoint 登出点 处理应用或者身份服务器的登出


五、项目移植


笔者需要被集成的Web系统是基于Guns后台开发,版本是beetle版本,项目集成CAS基于spring-shiro-cas移植。


5.1 导包


<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-core</artifactId>
   <version>1.4.0</version>
   <exclusions>
       <exclusion>
           <artifactId>slf4j-api</artifactId>
           <groupId>org.slf4j</groupId>
       </exclusion>
   </exclusions>
</dependency>
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>io.buji</groupId>
    <artifactId>buji-pac4j</artifactId>
    <version>4.0.0</version>
</dependency>
<dependency>
    <groupId>org.pac4j</groupId>
    <artifactId>pac4j-cas</artifactId>
    <version>3.3.0</version>
</dependency>

5.2 配置

cas:
  client-name: app
  server:
   url: http://127.0.0.1:8080/cas
  project:
   url: http://127.0.0.1:8082/iotProject


5.3 重写认证和授权函数


/**
 * 认证
 *
 * @param authenticationToken
 * @return
 * @throws AuthenticationException
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)throws AuthenticationException {
    final Pac4jToken pac4jToken = (Pac4jToken) authenticationToken;
    final List<CommonProfile> commonProfileList = pac4jToken.getProfiles();
    final CommonProfile commonProfile = commonProfileList.get(0);
    logger.info("单点登录返回的信息" + commonProfile.toString());
//        final Pac4jPrincipal principal = new Pac4jPrincipal(commonProfileList,getPrincipalNameAttribute());
    UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
    User user = shiroFactory.user(commonProfile.getId());
    ShiroUser shiroUser = shiroFactory.shiroUser(user);
    final PrincipalCollection principalCollection = new SimplePrincipalCollection(shiroUser, getName());
    return new SimpleAuthenticationInfo(principalCollection,commonProfileList.hashCode());
}
/**
 * 授权/验权(todo 后续有权限在此增加)
 *
 * @param principals
 * @return
 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
    ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
    List<Long> roleList = shiroUser.getRoleList();
    Set<String> permissionSet = new HashSet<>();
    Set<String> roleNameSet = new HashSet<>();
    for (Long roleId : roleList) {
        List<String> permissions = shiroFactory.findPermissionsByRoleId(roleId);
        if (permissions != null) {
            for (String permission : permissions) {
                if (ToolUtil.isNotEmpty(permission)) {
                    permissionSet.add(permission);
                }
            }
        }
        String roleName = shiroFactory.findRoleNameByRoleId(roleId);
        roleNameSet.add(roleName);
    }
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    info.addStringPermissions(permissionSet);
    info.addRoles(roleNameSet);
    return info;
}

5.4 调试


5.4.1 CAS验证原理


该图出自CAS官网,传送门

1666265069449.jpg


5.4.2 单点登录流程分析


1.在浏览器中输入项目地址,servlet开始处理HTTP请求


2.过滤器链中的过滤器执行动作逻辑,在ShiroFilterFactoryBean工厂类中定义一个包含4个过滤器的过滤器Map表,分别是3个自定义和一个默认的过滤器UserFilter:


filterChainDefinitionMap.put("/", "securityFilter");
filterChainDefinitionMap.put("/callback", "callbackFilter");
filterChainDefinitionMap.put("/logout", "logoutFilter");
filterChainDefinitionMap.put("/**","user");

3.在securityFilter中,此时还没有任何用户的信息,仅仅是将访问的服务网站重定向到CAS服务器登陆地址,http://127.0.0.1:8080/cas/login?service=http%3A%2F%2F127.0.0.1%3A8082%2FiotProject%2Fcallback%3Fclient_name%3Dapp


4.在登陆网页上填写用户名和密码信息后,继续执行过滤器callbackFilter,该过滤器的功能是利用CasAuthenticator验证ticket获取到中央认证服务器上用户的身份信息,接着BaseClient创建用户信息UserProfile,并将用户信息保存到Session中完成信息的共享,在保存的函数中完成用户主体身份login的流程,完成后重定向到受保护的网站即我们的服务网站


5.请求再次进到过滤器链中,因为服务地址对应的后台访问接口为“/”,对应着主页,先来看一下该函数:


/**
 * 跳转到主页
 */
@RequestMapping(value = "/", method = RequestMethod.GET)
public String index(Model model, HttpServletRequest request, HttpServletResponse response) {
    //获取当前用户角色列表
    ShiroUser user = ShiroKit.getUserNotNull();
    List<Long> roleList = user.getRoleList();
    if (roleList == null || roleList.size() == 0) {
        ShiroKit.getSubject().logout();
        model.addAttribute("tips", "该用户没有角色,无法登陆");
        return "/login.html";
    }
    List<MenuNode> menus = userService.getUserMenuNodes(roleList);
    model.addAttribute("menus", menus);
    return "/index.html";
}

因此又会进入到securityFilter过滤器中,此时用户已经完成认证,认证成功后直接放行进到后台拦截器中即对应的接口函数中,后续需要用到权限的请求doGetAuthorizationInfo()函数即可,至此完成单点登录功能。


5.5 完成


相关文章
|
1月前
|
JSON API 数据处理
Winform管理系统新飞跃:无缝集成SqlSugar与Web API,实现数据云端同步的革新之路!
【8月更文挑战第3天】在企业应用开发中,常需将Winform桌面应用扩展至支持Web API调用,实现数据云端同步。本文通过实例展示如何在已有SqlSugar为基础的Winform系统中集成HTTP客户端调用Web API。采用.NET的`HttpClient`处理请求,支持异步操作。示例包括创建HTTP辅助类封装请求逻辑及在Winform界面调用API更新UI。此外,还讨论了跨域与安全性的处理策略。这种方法提高了系统的灵活性与扩展性,便于未来的技术演进。
136 2
|
10天前
|
安全 Java API
【性能与安全的双重飞跃】JDK 22外部函数与内存API:JNI的继任者,引领Java新潮流!
【9月更文挑战第7天】JDK 22外部函数与内存API的发布,标志着Java在性能与安全性方面实现了双重飞跃。作为JNI的继任者,这一新特性不仅简化了Java与本地代码的交互过程,还提升了程序的性能和安全性。我们有理由相信,在外部函数与内存API的引领下,Java将开启一个全新的编程时代,为开发者们带来更加高效、更加安全的编程体验。让我们共同期待Java在未来的辉煌成就!
36 11
|
11天前
|
安全 Java API
【本地与Java无缝对接】JDK 22外部函数和内存API:JNI终结者,性能与安全双提升!
【9月更文挑战第6天】JDK 22的外部函数和内存API无疑是Java编程语言发展史上的一个重要里程碑。它不仅解决了JNI的诸多局限和挑战,还为Java与本地代码的互操作提供了更加高效、安全和简洁的解决方案。随着FFM API的逐渐成熟和完善,我们有理由相信,Java将在更多领域展现出其强大的生命力和竞争力。让我们共同期待Java编程新纪元的到来!
34 11
|
17天前
|
机器学习/深度学习 存储 前端开发
实战揭秘:如何借助TensorFlow.js的强大力量,轻松将高效能的机器学习模型无缝集成到Web浏览器中,从而打造智能化的前端应用并优化用户体验
【8月更文挑战第31天】将机器学习模型集成到Web应用中,可让用户在浏览器内体验智能化功能。TensorFlow.js作为在客户端浏览器中运行的库,提供了强大支持。本文通过问答形式详细介绍如何使用TensorFlow.js将机器学习模型带入Web浏览器,并通过具体示例代码展示最佳实践。首先,需在HTML文件中引入TensorFlow.js库;接着,可通过加载预训练模型如MobileNet实现图像分类;然后,编写代码处理图像识别并显示结果;此外,还介绍了如何训练自定义模型及优化模型性能的方法,包括模型量化、剪枝和压缩等。
25 1
|
17天前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
28 1
|
26天前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
27天前
|
Java
【Java集合类面试十二】、HashMap为什么线程不安全?
HashMap在并发环境下执行put操作可能导致循环链表的形成,进而引起死循环,因而它是线程不安全的。
|
27天前
|
安全 算法 Java
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
这篇文章讨论了Java集合类的线程安全性,列举了线程不安全的集合类(如HashSet、ArrayList、HashMap)和线程安全的集合类(如Vector、Hashtable),同时介绍了Java 5之后提供的java.util.concurrent包中的高效并发集合类,如ConcurrentHashMap和CopyOnWriteArrayList。
【Java集合类面试二】、 Java中的容器,线程安全和线程不安全的分别有哪些?
|
29天前
|
安全 Java 测试技术
深入探讨Java安全编程的最佳实践,帮助开发者保障应用的安全性
在网络安全日益重要的今天,确保Java应用的安全性成为了开发者必须面对的课题。本文介绍Java安全编程的最佳实践,包括利用FindBugs等工具进行代码审查、严格验证用户输入以防攻击、运用输出编码避免XSS等漏洞、实施访问控制确保授权访问、采用加密技术保护敏感数据等。此外,还强调了使用最新Java版本、遵循最小权限原则及定期安全测试的重要性。通过这些实践,开发者能有效提升Java应用的安全防护水平。
27 1
|
1月前
|
jenkins 持续交付 开发工具
"引爆效率革命!Docker+Jenkins+GIT+Tomcat:解锁持续集成魔法,一键部署Java Web应用的梦幻之旅!"
【8月更文挑战第9天】随着软件开发复杂度的增加,自动化变得至关重要。本文通过实例展示如何结合Docker、Jenkins、Git与Tomcat建立高效的持续集成(CI)流程。Docker确保应用环境一致性;Jenkins自动化处理构建、测试和部署;Git管理源代码版本;Tomcat部署Web应用。在Jenkins中配置Git插件并设置项目,集成Docker构建Tomcat应用镜像并运行容器。此外,通过自动化测试、代码质量检查、环境隔离和日志监控确保CI流程顺畅,从而显著提高开发效率和软件质量。
54 3

热门文章

最新文章