百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

深入解析 Apache Shiro 授权认证体系原理,解决开发难题

ccwgpt 2025-05-08 17:09 23 浏览 0 评论

你在做互联网后端开发的时候,有没有遇到过这样的情况:用户登录系统后,不同用户对各种功能模块的访问权限管理起来特别麻烦。有的功能普通用户不能访问,只有管理员可以,可怎么准确无误地实现这种权限控制呢?这其实就是授权认证体系要解决的关键问题,而 Apache Shiro 在这方面堪称一把利器。

Apache Shiro介绍

Apache Shiro 是一个强大且易用的 Java 安全框架,它提供了身份验证、授权、加密和会话管理等功能。在众多互联网项目中,随着业务复杂度增加,对用户权限的精细化管理需求越来越高。传统的自己编写简单的权限控制代码,不仅容易出错,而且扩展性差。Apache Shiro 的出现,就是为了给开发者提供一套全面且易于集成的安全解决方案。它可以轻松集成到各种 Java Web 应用、企业级应用中,让开发者专注于业务逻辑,而不必在底层的安全认证和授权机制上耗费过多精力。

认证流程

当用户在前端输入用户名和密码登录时,这些信息会被封装成一个AuthenticationToken传递到后端。常见的AuthenticationToken实现类如UsernamePasswordToken,它携带了用户名和密码信息。在后端,Shiro 的Subject代表当前执行操作的用户,它调用login方法,将AuthenticationToken交给SecurityManager。

从源码角度来看,Subject的login方法实际会调用SecurityManager的login方法。在SecurityManager类中,关键代码如下:

public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
    // 这里开始认证流程
    AuthenticationInfo info = authenticate(token);
    Subject loggedIn = createSubject(token, info, subject);
    onSuccessfulLogin(token, info, loggedIn);
    return loggedIn;
}

SecurityManager会委托配置好的Realm去验证用户身份。Realm就像是一个数据仓库,它从数据库或者其他数据源中获取用户的真实身份信息和密码等凭证。比如,如果用户名为 “admin”,Realm会去数据库中查询 “admin” 对应的密码。在实际应用中,为了保证安全性,密码通常不会以明文形式存储,而是经过加密处理,如使用 SHA - 256 等加密算法。

自定义Realm时,通常会继承AuthorizingRealm抽象类,并重写doGetAuthenticationInfo方法,示例代码如下:

public class CustomRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 授权相关逻辑,这里先不展开
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        // 从数据库查询用户信息,这里假设使用JDBC
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = DriverManager.getConnection(url, username, password);
            String sql = "SELECT password FROM users WHERE username =?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, username);
            rs = ps.executeQuery();
            if (rs.next()) {
                String encryptedPassword = rs.getString("password");
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                        username, encryptedPassword, getName());
                return info;
            } else {
                throw new UnknownAccountException("用户不存在");
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new AuthenticationException("数据库查询异常");
        } finally {
            // 关闭资源
            try {
                if (rs!= null) rs.close();
                if (ps!= null) ps.close();
                if (conn!= null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

Realm从数据库中获取到加密后的密码,然后将前端传来的密码也按照相同的加密算法进行加密,再比对两者是否一致。如果一致,认证就通过了。

在这个过程中,还可能涉及到一些额外的验证环节,例如多因素认证。假设系统开启了短信验证码功能,当用户输入用户名和密码后,Realm在验证密码的同时,还会检查用户输入的短信验证码是否正确,只有两者都匹配,认证才会通过。这时候可以在doGetAuthenticationInfo方法中添加对短信验证码的验证逻辑。

授权流程

用户认证通过后,就涉及到授权。比如,普通用户不能访问系统的用户管理模块,只有管理员可以。Shiro 的SecurityManager会根据用户的身份信息,从Realm中获取用户所拥有的角色和权限信息。

在 Shiro 中,权限的定义非常灵活,可以是简单的字符串表示,如 “user:manage” 表示对用户管理功能的操作权限,也可以是更复杂的基于资源和操作的权限描述。例如,“document:read:123” 表示对编号为 123 的文档有读取权限。

从源码层面分析,当判断用户是否具有某个权限时,会调用SecurityManager的isPermitted方法,该方法最终会委托给Authorizer进行权限判断。在AuthorizingRealm类中,与授权相关的关键方法是doGetAuthorizationInfo,当需要判断用户权限时会调用此方法来获取用户的角色和权限信息。

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    String username = (String) principals.getPrimaryPrincipal();
    // 从数据库查询用户角色和权限,这里假设使用JDBC
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    try {
        conn = DriverManager.getConnection(url, username, password);
        // 查询用户角色
        String roleSql = "SELECT role_name FROM user_roles WHERE username =?";
        ps = conn.prepareStatement(roleSql);
        ps.setString(1, username);
        rs = ps.executeQuery();
        List<String> roleList = new ArrayList<>();
        while (rs.next()) {
            String role = rs.getString("role_name");
            roleList.add(role);
        }
        info.setRoles(new HashSet<>(roleList));

        // 查询用户权限
        String permissionSql = "SELECT permission FROM role_permissions WHERE role_name IN (";
        for (int i = 0; i < roleList.size(); i++) {
            if (i > 0) {
                permissionSql += ", ";
            }
            permissionSql += "?";
        }
        permissionSql += ")";
        ps = conn.prepareStatement(permissionSql);
        for (int i = 0; i < roleList.size(); i++) {
            ps.setString(i + 1, roleList.get(i));
        }
        rs = ps.executeQuery();
        List<String> permissionList = new ArrayList<>();
        while (rs.next()) {
            String permission = rs.getString("permission");
            permissionList.add(permission);
        }
        info.setStringPermissions(new HashSet<>(permissionList));
        return info;
    } catch (SQLException e) {
        e.printStackTrace();
        throw new AuthorizationException("数据库查询异常");
    } finally {
        // 关闭资源
        try {
            if (rs!= null) rs.close();
            if (ps!= null) ps.close();
            if (conn!= null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Realm从数据库或者其他数据源中获取用户对应的角色和权限信息。数据库表结构通常会设计用户表、角色表、用户角色关联表以及权限表、角色权限关联表。当用户尝试访问某个功能时,Shiro 会检查该用户是否具有相应的权限。比如用户尝试访问用户管理页面,Shiro 会从Realm获取该用户的权限列表,判断其中是否包含 “user:manage” 权限,如果没有,就会阻止访问,并返回权限不足的提示。

而且,Shiro 还支持权限继承和组合。比如,一个高级管理员角色可能继承了普通管理员角色的所有权限,同时还拥有一些额外的权限。在进行权限检查时,Shiro 会递归地检查用户所拥有的角色及其继承角色的所有权限,确保授权判断的准确性。在Authorizer的实现类中,会有相应的逻辑来处理权限继承和组合的情况,这里不再详细展开源码分析。

总结

Apache Shiro 的授权认证体系原理虽然看起来有些复杂,但一旦理解并应用到项目中,会极大地提升系统的安全性和可维护性。各位后端开发的伙伴们,不妨在自己的下一个项目中尝试引入 Apache Shiro,体验它带来的便捷。如果你在使用过程中有任何问题或者心得,欢迎在评论区留言分享,让我们一起在开发的道路上不断进步。

相关推荐

一个基于.Net Core遵循Clean Architecture原则开源架构

今天给大家推荐一个遵循CleanArchitecture原则开源架构。项目简介这是基于Asp.netCore6开发的,遵循CleanArchitecture原则,可以高效、快速地构建基于Ra...

AI写代码翻车无数次,我发现只要提前做好这3步,bug立减80%

写十万行全是bug之后终于找到方法了开发"提示词管理助手"新版本那会儿,我差点被bug整崩溃。刚开始两周,全靠AI改代码架构,结果十万行程序漏洞百出。本来以为AI说没问题就稳了,结果...

OneCode低代码平台的事件驱动设计:架构解析与实践

引言:低代码平台的事件驱动范式在现代软件开发中,事件驱动架构(EDA)已成为构建灵活、松耦合系统的核心范式。OneCode低代码平台通过创新性的注解驱动设计,将事件驱动理念深度融入平台架构,实现了业务...

国内大厂AI插件评测:根据UI图生成Vue前端代码

在IDEA中安装大厂的AI插件,打开ruoyi增强项目:yudao-ui-admin-vue31.CodeBuddy插件登录腾讯的CodeBuddy后,大模型选择deepseek-v3,输入提示语:...

AI+低代码技术揭秘(二):核心架构

本文档介绍了为VTJ低代码平台提供支持的基本架构组件,包括Engine编排层、Provider服务系统、数据模型和代码生成管道。有关UI组件库和widget系统的信息,请参阅UI...

GitDiagram用AI把代码库变成可视化架构图

这是一个名为gitdiagram的开源工具,可将GitHub仓库实时转换为交互式架构图,帮助开发者快速理解代码结构。核心功能一键可视化:替换GitHubURL中的"hub...

30天自制操作系统:第六天:代码架构整理与中断处理

1.拆开bootpack.c文件。根据设计模式将对应的功能封装成独立的文件。2.初始化pic:pic(可编程中断控制器):在设计上,cpu单独只能处理一个中断。而pic是将8个中断信号集合成一个中断...

AI写代码越帮越忙?2025年研究揭露惊人真相

近年来,AI工具如雨后春笋般涌现,许多人开始幻想程序员的未来就是“对着AI说几句话”,就能轻松写出完美的代码。然而,2025年的一项最新研究却颠覆了这一期待,揭示了一个令人意外的结果。研究邀请了16位...

一键理解开源项目:两个自动生成GitHub代码架构图与说明书工具

一、GitDiagram可以一键生成github代码仓库的架构图如果想要可视化github开源项目:https://github.com/luler/reflex_ai_fast,也可以直接把域名替换...

5分钟掌握 c# 网络通讯架构及代码示例

以下是C#网络通讯架构的核心要点及代码示例,按协议类型分类整理:一、TCP协议(可靠连接)1.同步通信//服务器端usingSystem.Net.Sockets;usingTcpListene...

从复杂到优雅:用建造者和责任链重塑代码架构

引用设计模式是软件开发中的重要工具,它为解决常见问题提供了标准化的解决方案,提高了代码的可维护性和可扩展性,提升了开发效率,促进了团队协作,提高了软件质量,并帮助开发者更好地适应需求变化。通过学习和应...

低代码开发当道,我还需要学习LangChain这些框架吗?| IT杂谈

专注LLM深度应用,关注我不迷路前两天有位兄弟问了个问题:当然我很能理解这位朋友的担忧:期望效率最大化,时间用在刀刃上,“不要重新发明轮子”嘛。铺天盖地的AI信息轰炸与概念炒作,很容易让人浮躁与迷茫。...

框架设计并不是简单粗暴地写代码,而是要先弄清逻辑

3.框架设计3.框架设计本节我们要开发一个UI框架,底层以白鹭引擎为例。框架设计的第一步并不是直接撸代码,而是先想清楚设计思想,抽象。一个一个的UI窗口是独立的吗?不是的,...

大佬用 Avalonia 框架开发的 C# 代码 IDE

AvalonStudioAvalonStudio是一个开源的跨平台的开发编辑器(IDE),AvalonStudio的目标是成为一个功能齐全,并且可以让开发者快速使用的IDE,提高开发的生产力。A...

轻量级框架Lagent 仅需20行代码即可构建自己的智能代理

站长之家(ChinaZ.com)8月30日消息:Lagent是一个专注于基于LLM模型的代理开发的轻量级框架。它的设计旨在简化和提高这种模型下代理的开发效率。LLM模型是一种强大的工具,可以...

取消回复欢迎 发表评论: