得当才最美:Shiro安全框架使专心得

打印 上一主题 下一主题

主题 556|帖子 556|积分 1668

大家好,我是 V 哥。Apache Shiro 是一个强大且灵活的 Java 安全框架,专注于提供认证、授权、会话管理和加密功能。它常用于掩护 Java 应用的访问控制,特别是在 Web 应用中。相比于 Spring Security,Shiro 的设计更简洁,得当轻量级应用,并且在很多方面具有更好的易用性和扩展性,今天 V 哥就来聊聊 Shiro 安全框架。
Shiro 的核心概念

按照惯例,和 V 哥一起来了解一下 Shiro 的核心概念:

  • Subject
    Subject 是 Shiro 框架中一个核心的接口,表示应用中的“用户”或“实体”,用于交互和存储认证状态。通常通过 SecurityUtils.getSubject() 获取当前的 Subject。它代表了用户的身份信息和权限数据。
  • SecurityManager
    SecurityManager 是 Shiro 的核心控制器,负责管理所有的安全操作和认证。通过配置 SecurityManager,可以控制用户的认证、授权、会话等管理。
  • Realm
    Realm 是 Shiro 从数据源获取用户、角色和权限信息的途径。通过实现自界说的 Realm,可以将 Shiro 与数据库、LDAP、文件等数据源整合。Shiro 会把用户的认证和授权数据从 Realm 中获取。
  • Session
    Shiro 自带会话管理,不依赖于 Servlet 容器提供的会话。即使在非 Web 环境下,也可以使用 Shiro 的会话管理。Shiro 的会话管理提供了更过细的控制,比如会话超时、存储和共享等功能。
  • Authentication(认证)
    认证是指验证用户身份的过程。Shiro 提供了简朴的 API 来实现认证过程,比如 subject.login(token)。在现实应用中,通常通过用户名和密码的组合进行认证,但 Shiro 也支持其他方式(如 OAuth2、JWT 等)。
  • Authorization(授权)
    授权是指验证用户是否具备某些权限或角色的过程。Shiro 支持基于角色和基于权限的授权,允许更精细的权限控制。通过 subject.hasRole 或 subject.isPermitted 方法,开发者可以查抄用户的角色和权限。
  • Cryptography(加密)
    Shiro 内置了加密功能,提供对密码和敏感信息的加密和解密支持。它支持多种加密算法,并且在密码存储时支持散列和盐值。
Shiro 的重要功能和优势

V 哥总结几点Shiro 的重要功能和优势,这个在口试时吹牛逼用得到。

  • 易于集成
    Shiro 的 API 设计简朴,易于集成到各种 Java 应用中。开发者可以基于 Shiro 提供的默认实现快速搭建一个基本的安全架构,也可以根据需要自界说各种功能。
  • 独立的会话管理
    与基于 Web 容器的会话管理不同,Shiro 提供了跨环境的会话管理,可以应用于 Web 和非 Web 的环境,增加了应用的灵活性。
  • 权限控制简朴而灵活
    Shiro 的权限管理可以通过配置文件、注解或代码实现,提供了细粒度的访问控制。通过权限和角色的组合,开发者可以非常灵活地控制访问权限。
  • 支持多种数据源
    Shiro 可以从多种数据源(如数据库、LDAP、文件等)获取用户和权限信息,方便与各种现有系统整合。
  • 支持 Web 和非 Web 环境
    Shiro 不仅可以在 Web 应用中使用,也支持在桌面应用或微服务等环境中使用。
Shiro 的基本使用示例

光讲概念不是 V 哥风格,接下来,通过一个典型的 Shiro 应用来了解一下如何使用,包含配置 SecurityManager、配置 Realm、进行认证和授权等步调。

  • 配置 Shiro 环境
    可以通过 shiro.ini 文件配置 Shiro,也可以通过代码进行配置。
  1.    [main]
  2.    # 配置 SecurityManager
  3.    securityManager = org.apache.shiro.mgt.DefaultSecurityManager
  4.    # 配置 Realm
  5.    myRealm = com.wg.MyCustomRealm
  6.    securityManager.realms = $myRealm
复制代码

  • 创建自界说 Realm
    自界说 Realm 通过继承 AuthorizingRealm 并实现 doGetAuthenticationInfo 和 doGetAuthorizationInfo 方法来提供用户和权限数据。
  1.    public class MyCustomRealm extends AuthorizingRealm {
  2.        @Override
  3.        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  4.            // 获取用户名和密码等信息,查询数据库进行认证
  5.            return new SimpleAuthenticationInfo(username, password, getName());
  6.        }
  7.        @Override
  8.        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  9.            // 获取用户角色和权限信息
  10.            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
  11.            info.addRole("admin");
  12.            info.addStringPermission("user:read");
  13.            return info;
  14.        }
  15.    }
复制代码

  • 使用 Shiro 进行认证和授权
  1.    Subject currentUser = SecurityUtils.getSubject();
  2.    if (!currentUser.isAuthenticated()) {
  3.        UsernamePasswordToken token = new UsernamePasswordToken("username", "password");
  4.        try {
  5.            currentUser.login(token);
  6.            System.out.println("认证成功");
  7.        } catch (AuthenticationException ae) {
  8.            System.out.println("认证失败");
  9.        }
  10.    }
  11.    // 检查权限
  12.    if (currentUser.hasRole("admin")) {
  13.        //用输出模拟一下哈
  14.        System.out.println("用户拥有 admin 角色");
  15.    }
  16.    if (currentUser.isPermitted("user:read")) {
  17.        //用输出模拟一下哈
  18.        System.out.println("用户具有 user:read 权限");
  19.    }
复制代码
通过这个简朴的案例学习,咱们可以了解 Shiro 的基本使用,但这不是全部,听V哥继续慢慢道来。
场景案例

这点很紧张,强调一下哈,Shiro 得当需要简洁易用、安全控制要求灵活的 Java 应用,如中小型 Web 应用、桌面应用、分布式微服务等。对于大型企业应用或需要集成多种认证方式(如 OAuth2、JWT 等)的项目,Spring Security 可能会更符合。
要在微服务架构中实现基于 Apache Shiro 的安全认证和授权,比如一个订单管理系统为例。这个系统包含两个重要服务:

  • 用户服务:负责用户的注册、登录、认证等操作。
  • 订单服务:允许用户创建、查看、删除订单,并限制访问权限。
咱们来看一下,这个应该怎么设计呢?
微服务案例设计

在这个场景中,我们需要以下几项核心功能:

  • 用户认证:用户通过用户名和密码登录。
  • 权限控制:仅管理员能删除订单,普通用户只能查看和创建订单。
  • Token机制:使用 JWT Token(JSON Web Token)来管理用户的登录状态,实现无状态认证,使得在分布式环境下不依赖于单一会话。
  • 跨服务认证:订单服务在接收到哀求时,查抄并验证用户的身份和权限。
架构和技能选型


  • Spring Boot:用于快速搭建微服务。
  • Shiro:实现认证、授权。
  • JWT:生成和验证 Token,保持无状态认证。
  • Spring Data JPA:访问数据库存储用户和订单数据。
系统结构
  1. +------------------+      +---------------------+
  2. |   用户服务        |      |     订单服务        |
  3. |                  |      |                     |
  4. | 用户注册、登录    |      |   查看、创建、删除订单|
  5. +------------------+      +---------------------+
  6.             |                    |
  7.             |----用户 Token ------|
复制代码
步调:实现微服务中的认证和授权

1. 引入须要的依赖

在 pom.xml 文件中,添加 Shiro、JWT 和 Spring Data JPA 等依赖:
  1. <dependency>
  2.     <groupId>org.apache.shiro</groupId>
  3.     <artifactId>shiro-spring</artifactId>
  4.     <version>1.8.0</version>
  5. </dependency>
  6. <dependency>
  7.     <groupId>io.jsonwebtoken</groupId>
  8.     <artifactId>jjwt</artifactId>
  9.     <version>0.9.1</version>
  10. </dependency>
  11. <dependency>
  12.     <groupId>org.springframework.boot</groupId>
  13.     <artifactId>spring-boot-starter-data-jpa</artifactId>
  14. </dependency>
  15. <dependency>
  16.     <groupId>org.springframework.boot</groupId>
  17.     <artifactId>spring-boot-starter-web</artifactId>
  18. </dependency>
  19. <dependency>
  20.     <groupId>com.h2database</groupId>
  21.     <artifactId>h2</artifactId>
  22. </dependency>
复制代码
2. 配置 Shiro 与 JWT 过滤器

使用 Shiro 的自界说 JWT 过滤器实现无状态认证,通过 Token 验证用户。
  1. public class JwtFilter extends BasicHttpAuthenticationFilter {
  2.     @Override
  3.     protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
  4.         HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  5.         String token = httpServletRequest.getHeader("Authorization");
  6.         if (StringUtils.isBlank(token)) {
  7.             return false;
  8.         }
  9.         try {
  10.             // 解析 JWT token
  11.             JwtToken jwtToken = new JwtToken(token);
  12.             getSubject(request, response).login(jwtToken);
  13.             return true;
  14.         } catch (Exception e) {
  15.             return false;
  16.         }
  17.     }
  18. }
复制代码
3. 实现自界说 Realm

自界说 Realm,从数据库获取用户和角色信息,并使用 JWT Token 进行无状态认证。
  1. public class JwtRealm extends AuthorizingRealm {
  2.     @Override
  3.     public boolean supports(AuthenticationToken token) {
  4.         return token instanceof JwtToken;
  5.     }
  6.     @Override
  7.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  8.         String jwtToken = (String) token.getPrincipal();
  9.         // 验证 Token
  10.         String username = JwtUtil.getUsernameFromToken(jwtToken);
  11.         if (username == null) {
  12.             throw new AuthenticationException("Token 无效");
  13.         }
  14.         // 查询用户
  15.         User user = userService.findByUsername(username);
  16.         if (user == null) {
  17.             throw new AuthenticationException("用户不存在");
  18.         }
  19.         return new SimpleAuthenticationInfo(jwtToken, jwtToken, getName());
  20.     }
  21.     @Override
  22.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  23.         String username = JwtUtil.getUsernameFromToken(principals.toString());
  24.         SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
  25.         User user = userService.findByUsername(username);
  26.         // 添加角色和权限
  27.         authorizationInfo.addRole(user.getRole());
  28.         authorizationInfo.addStringPermission(user.getPermission());
  29.         return authorizationInfo;
  30.     }
  31. }
复制代码
4. 创建 JWT 工具类

编写一个工具类,用于生成和解析 JWT Token。
  1. public class JwtUtil {
  2.     //这里替换一下你自己的secret_key
  3.     private static final String SECRET_KEY = "这里打码了";
  4.     public static String generateToken(String username) {
  5.         return Jwts.builder()
  6.                 .setSubject(username)
  7.                 .setIssuedAt(new Date())
  8.                 .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 hour
  9.                 .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
  10.                 .compact();
  11.     }
  12.     public static String getUsernameFromToken(String token) {
  13.         Claims claims = Jwts.parser()
  14.                 .setSigningKey(SECRET_KEY)
  15.                 .parseClaimsJws(token)
  16.                 .getBody();
  17.         return claims.getSubject();
  18.     }
  19.     public static boolean isTokenExpired(String token) {
  20.         Claims claims = Jwts.parser()
  21.                 .setSigningKey(SECRET_KEY)
  22.                 .parseClaimsJws(token)
  23.                 .getBody();
  24.         return claims.getExpiration().before(new Date());
  25.     }
  26. }
复制代码
5. 编写用户服务和订单服务接口

用户服务接口

用户服务提供注册和登录 API。
  1. @RestController
  2. @RequestMapping("/user")
  3. public class UserController {
  4.     @PostMapping("/register")
  5.     public ResponseEntity<?> register(@RequestBody User user) {
  6.         userService.save(user);
  7.         return ResponseEntity.ok("用户注册成功");
  8.     }
  9.     @PostMapping("/login")
  10.     public ResponseEntity<?> login(@RequestBody User user) {
  11.         User dbUser = userService.findByUsername(user.getUsername());
  12.         if (dbUser != null && dbUser.getPassword().equals(user.getPassword())) {
  13.             String token = JwtUtil.generateToken(user.getUsername());
  14.             return ResponseEntity.ok(token);
  15.         }
  16.         return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("登录失败");
  17.     }
  18. }
复制代码
订单服务接口

订单服务在操作订单时会验证用户的角色和权限。
  1. @RestController
  2. @RequestMapping("/order")
  3. public class OrderController {
  4.     @GetMapping("/{orderId}")
  5.     public ResponseEntity<?> getOrder(@PathVariable Long orderId) {
  6.         Subject currentUser = SecurityUtils.getSubject();
  7.         if (currentUser.isPermitted("order:read")) {
  8.             // 查询订单
  9.             return ResponseEntity.ok("订单详情");
  10.         }
  11.         return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无权限查看订单");
  12.     }
  13.     @DeleteMapping("/{orderId}")
  14.     public ResponseEntity<?> deleteOrder(@PathVariable Long orderId) {
  15.         Subject currentUser = SecurityUtils.getSubject();
  16.         if (currentUser.hasRole("admin")) {
  17.             // 删除订单
  18.             return ResponseEntity.ok("订单已删除");
  19.         }
  20.         return ResponseEntity.status(HttpStatus.FORBIDDEN).body("无权限删除订单");
  21.     }
  22. }
复制代码
最后

这个案例中咱们通过如何使用 Shiro、JWT 和 Spring Boot 来构建一个无状态的微服务认证授权机制。通过 Shiro 实现用户认证和权限控制,使用 JWT 实现无状态 Token 验证。在轻量级的分布式微服务应用中,是不是使用 Shiro 感觉更加清新呢,欢迎批评区一起讨论,关注威哥爱编程,爱上Java,一辈子。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

星球的眼睛

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表