告别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 完成


相关文章
|
2天前
|
SQL 安全 Java
安全问题已经成为软件开发中不可忽视的重要议题。对于使用Java语言开发的应用程序来说,安全性更是至关重要
在当今网络环境下,Java应用的安全性至关重要。本文深入探讨了Java安全编程的最佳实践,包括代码审查、输入验证、输出编码、访问控制和加密技术等,帮助开发者构建安全可靠的应用。通过掌握相关技术和工具,开发者可以有效防范安全威胁,确保应用的安全性。
12 4
|
7天前
|
SQL 负载均衡 安全
安全至上:Web应用防火墙技术深度剖析与实战
【10月更文挑战第29天】在数字化时代,Web应用防火墙(WAF)成为保护Web应用免受攻击的关键技术。本文深入解析WAF的工作原理和核心组件,如Envoy和Coraza,并提供实战指南,涵盖动态加载规则、集成威胁情报、高可用性配置等内容,帮助开发者和安全专家构建更安全的Web环境。
19 1
|
9天前
|
安全 前端开发 Java
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第26天】Web安全是现代软件开发的重要领域,本文深入探讨了XSS和CSRF两种常见攻击的原理及防御策略。针对XSS,介绍了输入验证与转义、使用CSP、WAF、HTTP-only Cookie和代码审查等方法。对于CSRF,提出了启用CSRF保护、设置CSRF Token、使用HTTPS、二次验证和用户教育等措施。通过这些策略,开发者可以构建更安全的Web应用。
37 4
|
8天前
|
安全 Go PHP
Web安全进阶:XSS与CSRF攻击防御策略深度解析
【10月更文挑战第27天】本文深入解析了Web安全中的XSS和CSRF攻击防御策略。针对XSS,介绍了输入验证与净化、内容安全策略(CSP)和HTTP头部安全配置;针对CSRF,提出了使用CSRF令牌、验证HTTP请求头、限制同源策略和双重提交Cookie等方法,帮助开发者有效保护网站和用户数据安全。
32 2
|
11天前
|
存储 安全 Go
Web安全基础:防范XSS与CSRF攻击的方法
【10月更文挑战第25天】Web安全是互联网应用开发中的重要环节。本文通过具体案例分析了跨站脚本攻击(XSS)和跨站请求伪造(CSRF)的原理及防范方法,包括服务器端数据过滤、使用Content Security Policy (CSP)、添加CSRF令牌等措施,帮助开发者构建更安全的Web应用。
45 3
|
13天前
|
SQL 安全 Go
PHP在Web开发中的安全实践与防范措施###
【10月更文挑战第22天】 本文深入探讨了PHP在Web开发中面临的主要安全挑战,包括SQL注入、XSS攻击、CSRF攻击及文件包含漏洞等,并详细阐述了针对这些风险的有效防范策略。通过具体案例分析,揭示了安全编码的重要性,以及如何结合PHP特性与最佳实践来加固Web应用的安全性。全文旨在为开发者提供实用的安全指南,帮助构建更加安全可靠的PHP Web应用。 ###
30 1
|
16天前
|
Kubernetes 安全 应用服务中间件
动态威胁场景下赋能企业安全,F5推出BIG-IP Next Web应用防火墙
动态威胁场景下赋能企业安全,F5推出BIG-IP Next Web应用防火墙
36 3
|
1月前
|
安全 Java 编译器
Java 泛型深入解析:类型安全与灵活性的平衡
Java 泛型通过参数化类型实现了代码重用和类型安全,提升了代码的可读性和灵活性。本文深入探讨了泛型的基本原理、常见用法及局限性,包括泛型类、方法和接口的使用,以及上界和下界通配符等高级特性。通过理解和运用这些技巧,开发者可以编写更健壮和通用的代码。
|
1月前
|
缓存 安全 JavaScript
掌握JAMstack:构建更快、更安全的Web应用
JAMstack 是一种现代 Web 开发架构,结合 JavaScript、APIs 和 Markup,创建更快、更安全的 Web 应用。其核心优势包括高性能、安全性、可扩展性和易维护性。JAMstack 通过预构建静态页面和 API 实现高效渲染,利用静态站点生成器如 Gatsby 和 Next.js,并借助 CDN 和缓存策略提升全球访问速度。尽管面临复杂交互、SEO 和数据更新等挑战,但通过 Serverless Functions、预渲染和实时 API 更新等方案,这些挑战正逐步得到解决。
|
2月前
|
安全 Java API
java安全特性
java安全特性
28 8
下一篇
无影云桌面