深入理解Shiro安全框架:构建安全的Java应用程序

举报
bug菌 发表于 2024/09/29 15:43:12 2024/09/29
【摘要】 咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~🏆本文收录于「滚雪球学Java」专栏中,这个专栏专为有志于提升Java技能的你打造,覆盖Java编程的方方面面,助你从零基础到掌握Java开发的精髓。赶紧关注,收藏,学习吧!环境说明...

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~


🏆本文收录于「滚雪球学Java」专栏中,这个专栏专为有志于提升Java技能的你打造,覆盖Java编程的方方面面,助你从零基础到掌握Java开发的精髓。赶紧关注,收藏,学习吧!

环境说明:Windows 10 + IntelliJ IDEA 2021.3.2 + Jdk 1.8

前言

在信息化快速发展的今天,安全问题愈发重要,尤其是对于Web应用程序。保护用户的敏感数据和系统的完整性已经成为开发者们的首要任务。Apache Shiro作为一个强大且灵活的安全框架,能够帮助Java开发者实现身份验证、授权、加密和会话管理等功能。本文将深入探讨Shiro框架的基本概念、主要特性及其应用案例,并从深度和广度两个角度进行拓展,以便更好地理解和使用这一框架。

什么是Shiro?

Apache Shiro是一个开源的Java安全框架,它提供了简单易用的API,可以帮助开发者轻松实现应用程序的安全性。与其他安全框架相比,Shiro具有以下几个显著特点:

  • 灵活性:Shiro可以与任何Java应用程序集成,包括Java EE和Java SE应用。
  • 简单性:Shiro的API设计直观,开发者可以快速上手。
  • 功能全面:Shiro提供了身份验证、授权、会话管理和加密等多种安全功能。

Shiro的主要组成部分

在理解Shiro的工作机制之前,熟悉其组成部分是必要的。Shiro的主要组件包括:

  1. Subject(主体):可以是用户或应用程序,与应用程序进行交互的实体。
  2. Principal(主体标识):用于唯一标识主体的属性,如用户名或用户ID。
  3. Credentials(凭证):主体的认证信息,如密码或令牌。
  4. Realm(领域):用于定义如何获取主体的身份信息和权限的组件。每个Realm可以访问数据库、LDAP或其他数据源。
  5. SecurityManager(安全管理器):Shiro的核心,它协调所有的安全操作。
  6. Session(会话):Shiro提供的会话管理机制,可以用于存储与用户相关的信息。

Shiro的主要功能

1. 身份验证

Shiro的身份验证过程包括以下几个步骤:

  • 用户输入凭证:用户在登录页面输入用户名和密码。
  • Shiro进行验证:Shiro会将输入的凭证与Realm中的信息进行比对。
  • 返回结果:如果验证成功,用户将获得访问权限;如果失败,则会返回相应的错误信息。

身份验证的实现可以通过以下代码示例进行演示:

public class MyRealm extends AuthorizingRealm {
    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        // 模拟数据库查询
        if ("admin".equals(username) && "password".equals(password)) {
            return new SimpleAuthenticationInfo(username, password, getName());
        } else {
            throw new AuthenticationException("Invalid credentials");
        }
    }
}

2. 授权

授权是控制用户访问特定资源的过程。Shiro支持基于角色和基于权限的授权。通过定义角色和权限,开发者可以灵活地控制用户的访问权限。

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    String username = (String) principals.getPrimaryPrincipal();

    if ("admin".equals(username)) {
        info.addRole("admin");
        info.addStringPermission("manage");
    }
    return info;
}

3. 会话管理

Shiro提供了强大的会话管理功能,能够方便地管理用户的会话状态。开发者可以自定义会话超时、会话存储等配置,以确保应用程序的安全性和灵活性。

DefaultSessionManager sessionManager = new DefaultSessionManager();
sessionManager.setGlobalSessionTimeout(1800000); // 30分钟超时

4. 加密

在处理敏感信息时,加密是非常重要的一环。Shiro内置了多种加密算法,可以帮助开发者保护用户的密码和其他敏感数据。

SimpleHash hash = new SimpleHash("SHA-256", "password", ByteSource.Util.bytes("salt"), 1024);
String hashedPassword = hash.toHex();

Shiro的工作流程

Shiro的工作流程可以概括为以下几个步骤:

  1. 用户请求:用户向服务器发送请求,通常是登录请求。
  2. 安全管理器调用Realm:安全管理器会调用Realm进行身份验证和授权。
  3. 身份验证:Realm根据用户提供的凭证验证用户身份。
  4. 授权检查:如果身份验证通过,安全管理器会检查用户的授权信息。
  5. 响应用户请求:根据身份验证和授权结果,系统将相应的资源返回给用户。

演示案例

下面是一个简单的Shiro应用示例,展示了如何使用Shiro进行身份验证和授权。

1. Maven依赖

首先,确保在你的Maven项目中添加Shiro的依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.8.0</version>
</dependency>

2. 配置Realm

我们需要创建一个自定义的Realm,以便获取用户的身份和权限信息:

public class MyRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();
        
        // 根据用户名设置角色和权限
        if ("admin".equals(username)) {
            info.addRole("admin");
            info.addStringPermission("manage");
        } else if ("user".equals(username)) {
            info.addRole("user");
            info.addStringPermission("view");
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        // 模拟数据库查询
        if ("admin".equals(username) && "password".equals(password)) {
            return new SimpleAuthenticationInfo(username, password, getName());
        } else {
            throw new AuthenticationException("Invalid credentials");
        }
    }
}

代码解析:

  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

  如上这段代码是一个自定义的 Realm 类,继承自 AuthorizingRealm,用于在基于 Apache Shiro 的安全框架中处理用户的身份验证和授权。以下是代码的逐行解释:

  1. public class MyRealm extends AuthorizingRealm { ... }
    这行代码声明了一个名为 MyRealm 的公共类,它继承自 AuthorizingRealm 类。AuthorizingRealm 是 Shiro 框架中用于处理安全相关操作的基类。

  2. @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { ... }
    这个方法重写了 AuthorizingRealm 类中的 doGetAuthorizationInfo 方法,用于获取用户的授权信息,即用户的角色和权限。

  3. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    创建了一个 SimpleAuthorizationInfo 对象,用于存储用户的授权信息。

  4. String username = (String) principals.getPrimaryPrincipal();
    PrincipalCollection 中获取用户的主要身份信息(通常是用户名)。

  5. if ("admin".equals(username)) { ... } else if ("user".equals(username)) { ... }
    根据用户名设置角色和权限。如果用户名是 “admin”,则添加 “admin” 角色和 “manage” 权限;如果用户名是 “user”,则添加 “user” 角色和 “view” 权限。

  6. return info;
    返回包含用户授权信息的 SimpleAuthorizationInfo 对象。

  7. @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { ... }
    这个方法重写了 AuthorizingRealm 类中的 doGetAuthenticationInfo 方法,用于获取用户的身份验证信息。

  8. String username = (String) token.getPrincipal();
    AuthenticationToken 中获取用户的身份信息(通常是用户名)。

  9. String password = new String((char[]) token.getCredentials());
    AuthenticationToken 中获取用户的凭据(密码),并将其转换为字符串。

  10. if ("admin".equals(username) && "password".equals(password)) { ... } else { ... }
    模拟数据库查询,如果用户名是 “admin” 且密码是 “password”,则返回一个 SimpleAuthenticationInfo 对象,包含用户名、密码和 Realm 名称;否则,抛出 AuthenticationException 异常。

  11. return new SimpleAuthenticationInfo(username, password, getName());
    返回包含用户身份验证信息的 SimpleAuthenticationInfo 对象。

  12. throw new AuthenticationException("Invalid credentials");
    如果用户凭据无效,则抛出 AuthenticationException 异常。

这个自定义 Realm 类提供了一个简单的示例,展示了如何在 Shiro 中实现基于用户名和密码的身份验证,以及如何根据用户名分配角色和权限。在实际应用中,身份验证信息通常会从数据库或其他持久化存储中检索,而不是硬编码在代码中。

3. 配置安全管理器

接下来,我们需要配置Shiro的安全管理器:

public class ShiroConfig {

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm());
        return securityManager;
    }

    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }
}

代码解析:

  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

  这段代码是使用 Spring Framework 和 Apache Shiro 进行安全配置的示例。它定义了一个名为 ShiroConfig 的类,其中包含了两个方法,每个方法都用 @Bean 注解标记,这意味着它们将被 Spring 容器管理,并在需要时自动注入到其他组件中。以下是代码的逐行解释:

  1. public class ShiroConfig { ... }
    这行代码声明了一个名为 ShiroConfig 的公共类。

  2. @Bean public SecurityManager securityManager() { ... }
    这个方法创建并配置了一个 SecurityManager 实例,这是 Shiro 框架中的核心组件,负责处理安全相关的操作。

  3. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    创建了一个 DefaultWebSecurityManager 对象,它是 Shiro 提供的一个默认的 SecurityManager 实现,专为 Web 应用程序设计。

  4. securityManager.setRealm(myRealm());
    将自定义的 Realm(MyRealm)设置到 SecurityManager 中。Realm 是 Shiro 框架中用于执行安全检查的组件,它负责认证(Authentication)和授权(Authorization)。

  5. return securityManager;
    返回配置好的 SecurityManager 实例。

  6. @Bean public MyRealm myRealm() { ... }
    这个方法创建并返回一个 MyRealm 实例。MyRealm 是前面讨论过的自定义 Realm 类,它负责处理用户的身份验证和授权逻辑。

  7. return new MyRealm();
    返回一个新的 MyRealm 实例。

在 Spring 应用程序中,这些 @Bean 方法将被 Spring 容器调用,以创建和配置 SecurityManagerMyRealm 实例。然后,这些实例可以被注入到需要它们的其他组件中,例如 Shiro 的过滤器链。

请注意,这段代码假设您的项目已经配置了 Spring 和 Shiro 的相关依赖,并且 MyRealm 类已经定义并位于项目的类路径中。此外,这段代码使用了 Spring 的注解来定义 Bean,这意味着它应该在 Spring 配置类中使用,或者在 Spring Boot 应用程序中作为 @Configuration 类的一部分。

4. 使用Shiro进行身份验证

在我们的服务层中,我们可以使用Shiro进行身份验证和授权:

public class AuthService {

    public boolean login(String username, String password) {
        Subject currentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            currentUser.login(token);
            return true;
        } catch (AuthenticationException e) {
            return false;
        }
    }

    public boolean hasPermission(String permission) {
        Subject currentUser = SecurityUtils.getSubject();
        return currentUser.isPermitted(permission);
    }
}

代码解析:

  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

  这段代码定义了一个名为 AuthService 的类,它提供了基于 Apache Shiro 的登录和权限检查功能。以下是代码的逐行解释:

  1. public class AuthService { ... }
    这行代码声明了一个名为 AuthService 的公共类。

  2. public boolean login(String username, String password) { ... }
    这个方法提供了用户登录的功能。它接受用户名和密码作为参数,并返回一个布尔值,表示登录是否成功。

  3. Subject currentUser = SecurityUtils.getSubject();
    这行代码获取当前的 Subject 实例。在 Shiro 中,Subject 代表当前的用户、系统或其他执行主体,它封装了认证和授权的状态。

  4. UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    创建了一个 UsernamePasswordToken 对象,它包含了用户名和密码信息,用于身份验证。

  5. try { ... } catch (AuthenticationException e) { ... }
    使用 try-catch 块来处理登录过程中可能抛出的异常。

  6. currentUser.login(token);
    调用 Subjectlogin 方法,传入之前创建的 UsernamePasswordToken 对象,以执行登录操作。

  7. return true;
    如果登录成功,没有抛出异常,则返回 true

  8. return false;
    如果登录失败,捕获到 AuthenticationException 异常,则返回 false

  9. public boolean hasPermission(String permission) { ... }
    这个方法提供了检查当前用户是否具有特定权限的功能。它接受一个权限字符串作为参数,并返回一个布尔值,表示用户是否具有该权限。

  10. Subject currentUser = SecurityUtils.getSubject();
    再次获取当前的 Subject 实例。

  11. return currentUser.isPermitted(permission);
    调用 SubjectisPermitted 方法,传入权限字符串,以检查当前用户是否具有该权限,并返回结果。

这个 AuthService 类提供了简单的登录和权限检查方法,可以在应用程序中用于安全相关的操作。在实际应用中,你可能需要更复杂的错误处理和更细粒度的安全控制。此外,这段代码假设 Shiro 的环境已经正确配置,并且 SecurityUtils 能够获取到有效的 Subject 实例。

5. 控制器示例

最后,我们可以在控制器中调用我们的服务:

@RestController
@RequestMapping("/api")
public class AuthController {

    @Autowired
    private AuthService authService;

    @PostMapping("/login")
    public ResponseEntity<String> login(@RequestBody UserLoginDto userLoginDto) {
        boolean success = authService.login(userLoginDto.getUsername(), userLoginDto.getPassword());
        return success ? ResponseEntity.ok("Login successful") : ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
    }

    @GetMapping("/resource")
    public ResponseEntity<String> accessResource() {
        if (authService.hasPermission("view")) {
            return ResponseEntity.ok("Access granted");
        } else {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied");
        }
    }
}

代码解析:

  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。

  这段代码是一个使用 Spring Framework 创建的 REST 控制器类 AuthController,它提供了两个 API 端点:一个用于处理用户登录 (/login),另一个用于访问受保护资源 (/resource)。以下是代码的逐行解释:

  1. @RestController
    这个注解表明该类是一个 REST 控制器,它组合了 @Controller@ResponseBody 注解的功能,意味着类中的方法返回的对象将直接作为 HTTP 响应的正文。

  2. @RequestMapping("/api")
    这个注解定义了类级别的路由。所有在该类中定义的方法都会在 URL 路径前加上 /api

  3. @Autowired
    这个注解自动注入 AuthService 类的实例到 AuthController 中。

  4. private AuthService authService;
    这行代码声明了一个 AuthService 类型的成员变量 authService

  5. @PostMapping("/login")
    这个注解将 login 方法映射到 HTTP POST 请求的 /api/login 路径。

  6. public ResponseEntity<String> login(@RequestBody UserLoginDto userLoginDto)
    login 方法接受一个 JSON 格式的请求体,该请求体被自动转换为 UserLoginDto 类的实例。

  7. boolean success = authService.login(userLoginDto.getUsername(), userLoginDto.getPassword());
    调用 AuthServicelogin 方法,传入用户名和密码,返回一个布尔值表示登录是否成功。

  8. return success ? ResponseEntity.ok("Login successful") : ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Login failed");
    根据登录成功与否,返回相应的 ResponseEntity 对象。如果成功,返回 200 OK 状态码和成功消息;如果失败,返回 401 Unauthorized 状态码和失败消息。

  9. @GetMapping("/resource")
    这个注解将 accessResource 方法映射到 HTTP GET 请求的 /api/resource 路径。

  10. public ResponseEntity<String> accessResource()
    accessResource 方法用于检查当前用户是否有权限访问受保护的资源。

  11. if (authService.hasPermission("view")) { ... } else { ... }
    调用 AuthServicehasPermission 方法,检查用户是否有 “view” 权限。

  12. return ResponseEntity.ok("Access granted");
    如果用户有权限,返回 200 OK 状态码和访问授权消息。

  13. return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Access denied");
    如果用户没有权限,返回 403 Forbidden 状态码和访问拒绝消息。

这个 AuthController 类提供了一个简单的 REST API,用于处理用户登录和资源访问权限检查。在实际应用中,你可能需要添加更多的安全措施,如 HTTPS、密码加密、更复杂的权限控制等。此外,UserLoginDto 类需要定义并包含 usernamepassword 属性,以便接收客户端发送的登录信息。

深度探讨Shiro的特性

1. 可扩展性

Shiro提供了高度的可扩展性,开发者可以根据业务需求扩展其功能。例如,开发者可以自定义Realm,支持多种身份验证机制(如OAuth、JWT等),并集成多种存储方式(如数据库、LDAP等)。

2. 支持多种认证方式

除了传统的用户名和密码,Shiro还支持多种认证方式,如基于令牌的认证、LDAP认证等。这使得Shiro在现代Web应用程序中的适用性更强,能够满足多种安全需求。

3. 细粒度的权限控制

Shiro支持细粒度的权限控制,开发者可以为每个操作定义权限。例如,某个用户可以被授权访问特定的API,而另一个

用户则不能访问。这种灵活性使得应用程序的安全性得到了增强。

实践中的最佳实践

在使用Shiro时,开发者应该遵循一些最佳实践,以确保应用程序的安全性:

  1. 使用安全的密码存储:在存储用户密码时,务必使用盐和哈希算法进行加密,避免明文存储。

  2. 定期更新权限和角色:随着业务的发展,用户的权限和角色可能会发生变化。定期审查和更新这些信息是必要的。

  3. 实现会话管理:在会话管理方面,开发者应确保会话超时设置合理,防止用户长时间未操作后仍保持登录状态。

  4. 使用HTTPS保护数据传输:确保在数据传输过程中使用HTTPS,以保护用户的敏感信息不被窃取。

  5. 定期进行安全测试:定期对应用程序进行安全测试和代码审查,以发现和修复潜在的安全漏洞。

结论

通过本文的介绍,我们了解了Apache Shiro框架的基本概念、主要功能以及如何在Java应用程序中使用Shiro进行安全管理。Shiro的灵活性和易用性使得它成为构建安全应用程序的理想选择。

希望通过本篇文章,读者能够对Shiro有更深入的理解,并能够在实际项目中灵活应用这一框架。随着安全需求的不断提升,掌握Shiro框架将为开发者在构建安全应用程序时提供强大的支持。无论是在小型项目还是大型企业级应用中,Shiro都能发挥其独特的价值,为开发者提供安全保障。

☀️建议/推荐你

无论你是计算机专业的学生,还是对编程有兴趣的小伙伴,都建议直接毫无顾忌的学习此专栏「滚雪球学Java」,bug菌郑重承诺,凡是学习此专栏的同学,均能获取到所需的知识和技能,全网最快速入门Java编程,就像滚雪球一样,越滚越大,指数级提升。

码字不易,如果这篇文章对你有所帮助,帮忙给bug菌来个一键三连(关注、点赞、收藏) ,您的支持就是我坚持写作分享知识点传播技术的最大动力。
  同时也推荐大家关注我的硬核公众号:「猿圈奇妙屋」 ;以第一手学习bug菌的首发干货,不仅能学习更多技术硬货,还可白嫖最新BAT大厂面试真题、4000G Pdf技术书籍、万份简历/PPT模板、技术文章Markdown文档等海量资料,你想要的我都有!

📣关于我

我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,掘金等平台签约作者,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计30w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。


–End

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。