返回

Spring Boot与Shiro集成JWT的简洁指南

后端

在现代Web应用程序开发中,安全性和认证至关重要。Spring Boot和Shiro是Java开发人员用来加强应用程序安全的流行框架。本文将深入探讨如何将Shiro与Spring Boot集成,并使用JSON Web令牌(JWT)进行身份验证。

前言

JWT是一种紧凑、自包含的令牌,用于在分布式系统中安全地传递身份验证信息。它由三部分组成:标头、有效载荷和签名。标头和有效载荷使用Base64编码,而签名则使用HMAC算法生成。

整合Shiro和JWT

要将Shiro与JWT集成,我们需要执行以下步骤:

  1. 添加依赖项: 在Maven或Gradle项目中添加以下依赖项:

    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring-boot-starter</artifactId>
        <version>2.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.19.2</version>
    </dependency>
    
  2. 配置Shiro:application.yml文件中配置Shiro:

    shiro:
        jwt:
            secret: "my-secret-key"  # 签名密钥
            expiration: 604800000  # 过期时间(单位:毫秒)
    
  3. 创建Realm: 创建实现Realm接口的自定义Realm:

    @Service
    public class JwtRealm implements Realm {
    
        @Override
        public String getName() {
            return "JwtRealm";
        }
    
        @Override
        public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String jwt = (String) token.getPrincipal();
            // 验证JWT
            if (validateJwt(jwt)) {
                // 从有效载荷中提取身份信息
                Claims claims = Jwts.parser()
                        .setSigningKey(shiro.getJwt().getSecret().getBytes())
                        .parseClaimsJws(jwt)
                        .getBody();
    
                String username = claims.getSubject();
                List<String> roles = (List<String>) claims.get("roles");
                List<String> permissions = (List<String>) claims.get("permissions");
    
                // 创建身份信息对象
                SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, jwt, getName());
                info.setRoles(new HashSet<>(roles));
                info.setPermissions(new HashSet<>(permissions));
    
                return info;
            }
    
            return null;
        }
    
        // 验证JWT
        private boolean validateJwt(String jwt) {
            try {
                Jwts.parser()
                        .setSigningKey(shiro.getJwt().getSecret().getBytes())
                        .parseClaimsJws(jwt);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }
    
  4. 配置JWT过滤器: 创建实现Filter接口的自定义JWT过滤器:

    @WebFilter(urlPatterns = "/**")
    public class JwtFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            String jwt = request.getHeader("Authorization");
            if (jwt != null && jwt.startsWith("Bearer ")) {
                jwt = jwt.substring(7);
                // 验证JWT
                if (validateJwt(jwt)) {
                    // 解析JWT并将其存储在安全上下文中
                    Claims claims = Jwts.parser()
                            .setSigningKey(shiro.getJwt().getSecret().getBytes())
                            .parseClaimsJws(jwt)
                            .getBody();
    
                    String username = claims.getSubject();
                    SecurityUtils.getSubject().login(new UsernamePasswordToken(username, jwt, false));
                }
            }
    
            chain.doFilter(request, response);
        }
    
        // 验证JWT
        private boolean validateJwt(String jwt) {
            try {
                Jwts.parser()
                        .setSigningKey(shiro.getJwt().getSecret().getBytes())
                        .parseClaimsJws(jwt);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
    }
    
  5. 注册Realm和过滤器: 在Spring Boot应用程序的main方法中注册Realm和过滤器:

    @SpringBootApplication
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
            SecurityUtils.setSecurityManager(new DefaultSecurityManager(new JwtRealm()));
            SecurityUtils.getSubject().getSession().start();
        }
    }
    

生成JWT

在认证成功后,我们可以使用Jwts库生成JWT:

// 生成JWT
String jwt = Jwts.builder()
        .setSubject(username)  // 设置主题(用户标识)
        .setIssuedAt(new Date())  // 设置签发时间
        .setExpiration(new Date(System.currentTimeMillis() + shiro.getJwt().getExpiration()))  // 设置过期时间
        .claim("roles", roles)  // 设置角色
        .claim("permissions", permissions)  // 设置权限
        .signWith(SignatureAlgorithm.HS256, shiro.getJwt().getSecret().getBytes())  // 设置签名算法和密钥
        .compact();

// 返回JWT
return jwt;

使用JWT

客户端可以将JWT作为Authorization头的一部分发送给服务器,格式为Bearer {jwt}。服务器将验证JWT并基于其内容授予访问权限。

总结

通过将Shiro与JWT集成,我们可以在Spring Boot应用程序中实现安全且无状态的身份验证。JWT的轻量级、自包含性和易于解析的特性使其成为分布式应用程序的理想选择。通过遵循本文中概述的步骤,开发者可以轻松地在他们的应用程序中实现JWT认证,从而增强其安全性并提高用户体验。