SpringBoot集成整合JWT与Shiro流程详解

下面是对于“SpringBoot集成整合JWT与Shiro流程详解”的完整攻略。

概述

在传统的Web应用中,我们通常采用用户名和密码进行身份认证,但这种方式很容易受到各种攻击,例如:暴力破解、钓鱼等。为了解决这些问题,我们可以采用JWT的方式进行身份认证,并使用Shiro进行授权管理。本文将详细介绍SpringBoot集成整合JWT与Shiro的流程。

JWT简介

JWT(JSON Web Token) 是一种基于 JSON 的开放标准(RFC 7519),用于通过网络传输包含声明的安全令牌。

JWT由三部分组成,使用‘.’进行分隔,分别为:头部(header)、载荷(payload)和签名(signature)。

JWT格式

头部(header).载荷(payload).签名(signature)

JWT特点

  • 去中心化:基于 TOKEN 进行身份认证,无需访问数据库或共享会话
  • 自包含:包含了所有的权利或声明,不需考虑设备或它的寄存器
  • 简洁:作为构件的传输, 便于POST参数或存储于URL中
  • 靠谱:数字签名保证鉴别使用者的可信性和不可否认性

Shiro简介

Shiro是一个Java安全框架,提供身份认证、授权、加密、会话管理等功能。Shiro的优点是简单易用,且不依赖Spring框架,可以与Spring等框架很好地进行集成。

整合步骤

下面是我们整合的步骤:

  1. 引入依赖
  2. 编写Shiro认证过滤器
  3. 编写JWT工具类
  4. 编写登录接口
  5. 编写需要鉴权的接口

1. 引入依赖

在pom.xml中引入以下依赖:

<!-- JWT -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<!-- Shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
</dependency>

2. 编写Shiro认证过滤器

创建Shiro认证过滤器,用于鉴权。这里我们先创建Controller和Service层,后面会用到。

Controller

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public ResultBean login(User user) {
        // 登录操作,省略...
    }

    @RequiresRoles("admin")
    @GetMapping("/admin")
    public ResultBean admin() {
        // 管理员操作,省略...
    }
}

Service

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public User findByUsername(String username) {
        // 查询操作,省略...
    }

    @Override
    public String generateToken(User user) {
        // JWT生成Token操作,省略...
    }
}

在Shiro中,每个请求都需要经过一个认证过滤器,我们可以通过继承AuthenticatingFilter实现自己的认证过滤器。

JWTFilter
public class JWTFilter extends AuthenticatingFilter {
    @Autowired
    private UserService userService;

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        String token = getToken(request);
        if (StringUtils.isBlank(token)) {
            return false;
        }

        try {
            JWTUtil.verify(token);
            String username = JWTUtil.getUsername(token);
            User user = userService.findByUsername(username);
            if (user == null) {
                return false;
            }

            // 设置当前请求用户信息
            SecurityUtils.getSubject().login(new UsernamePasswordToken(username, token));
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        return false;
    }

    private String getToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("token");
        }
        return StringUtils.removeStart(token, "Bearer ");
    }
}

3. 编写JWT工具类

在项目中,我们需要编写JWT生成和鉴权的工具类。

JWTUtil

public class JWTUtil {
    public static final String CLAIM_KEY_USERNAME = "sub";
    public static final String CLAIM_KEY_CREATED = "created";
    private static final String SECRET = "123456";
    private static final long EXPIRATION_TIME = 3600 * 24 * 7;

    /**
     * 生成JWT Token
     */
    public static String generateToken(User user) {
        Map<String, Object> claims = new HashMap<>(2);
        claims.put(Claims.SUBJECT, user.getUsername());
        claims.put(Claims.ISSUED_AT, new Date());
        return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, SECRET).compact();
    }

    /**
     * 从Token中获取用户名
     */
    public static String getUsername(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    /**
     * 校验Token是否合法
     */
    public static boolean verify(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + EXPIRATION_TIME * 1000);
    }

    private static <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private static Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
    }
}

4. 编写登录接口

在用户进行登录时,我们需要获取到前端传入的用户名和密码,进行验证操作。如果验证成功,我们就需要生成加密后的JWT Token,并将其返回给前端。

UserController

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping("/login")
    public ResultBean login(User user) {
        // 1. 进行 Shiro 登录操作
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            return ResultBean.error("账号密码错误!");
        }

        // 2. 生成 JWT Token
        user = userService.findByUsername(user.getUsername());
        String jwtToken = userService.generateToken(user);

        // 3. 将 Token 返回给前端
        return ResultBean.success(jwtToken);
    }
}

5. 编写需要鉴权的接口

最后,我们需要编写需要被鉴权的接口,例如管理员操作接口。

UserController

@RestController
public class UserController {
    @Autowired
    private UserService userService;

    @RequiresRoles("admin")
    @GetMapping("/admin")
    public ResultBean admin() {
        // 管理员操作,省略...
    }
}

示例

为了更好地理解我们上面的步骤,这里提供两个实际案例供参考。

示例一:登录接口

在项目中,我们有一个登录接口 /login,当用户输入正确的用户名和密码时,我们将会返回一个 JWT Token。

UserController 中,我们需要编写如下登录函数逻辑:

@PostMapping("/login")
public ResultBean login(User user) {
    // 1. 进行 Shiro 登录操作
    Subject subject = SecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
    try {
        subject.login(token);
    } catch (AuthenticationException e) {
        return ResultBean.error("账号密码错误!");
    }

    // 2. 生成 JWT Token
    user = userService.findByUsername(user.getUsername());
    String jwtToken = userService.generateToken(user);

    // 3. 将 Token 返回给前端
    return ResultBean.success(jwtToken);
}

示例二:管理员操作接口

在项目中,我们有一个管理员操作接口 /admin,只有具有 admin 角色的用户才能访问该接口。

UserController 中,我们需要在接口上标注必须具有 admin 角色的注解,即 @RequiresRoles("admin")

@RequiresRoles("admin")
@GetMapping("/admin")
public ResultBean admin() {
    // 进行管理员操作逻辑
    return ResultBean.success();
}

总结

通过本文的分享,相信大家学习了如何使用SpringBoot集成整合JWT与Shiro的流程。目前JWT和Shiro都是比较热门的安全框架,使用起来比较简便,也可以非常好的进行集成,方便我们进行项目开发。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot集成整合JWT与Shiro流程详解 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • NodeJS实现不可逆加密与密码密文保存的方法

    下面是“NodeJS实现不可逆加密与密码密文保存的方法”的完整攻略。 1. 什么是不可逆加密 不可逆加密(也称哈希函数)是一种将任意长度的输入(一般是明文)通过哈希算法变换成固定长度的输出(一般是密文)的方法,它的特点是不可逆性、唯一性、固定性、散列值分布性等,常用于实现密码的密文保存。 2. NodeJS中的常见哈希函数 在NodeJS中,常见的哈希函数包…

    Java 2023年5月23日
    00
  • 深入了解Java中的static关键字

    深入了解Java中的static关键字 在Java中,static是一个用于修饰变量、方法和内部类等的关键字。它表示这些成员属于类本身,而不是类的实例,因此,我们可以直接通过类名来调用这些成员,无需先实例化对象。 static变量 在Java中,静态变量是共享的、存储在堆区的变量。即,无论创建多少实例对象,它们都只有一个拷贝。我们可以通过类名加点的形式进行直…

    Java 2023年5月26日
    00
  • java 二维数组矩阵乘法的实现方法

    Java二维数组矩阵的乘法实现 矩阵的乘法是一种重要的运算,它是许多计算机程序中的基本操作之一。在Java中,我们可以使用二维数组来表示矩阵,并通过循环来实现矩阵的乘法运算。 矩阵乘法的基本原理 假设我们有两个矩阵A和B: A = [a11 a12 a13] [a21 a22 a23] B = [b11 b12] [b21 b22] [b31 b32] 这里…

    Java 2023年5月26日
    00
  • java 浅析代码块的由来及用法

    Java 浅析代码块的由来及用法 背景介绍 在Java中,代码块是一段静态或动态语句代码,在执行时会形成一个作用域。根据代码块的位置和声明方式,可以分为实例初始化块、静态初始化块和局部代码块。 实例初始化块 实例初始化块是被定义在类内部,但没有被声明为静态的代码块,可以在创建对象时被调用,用于对对象进行初始化操作。 public class Person {…

    Java 2023年5月30日
    00
  • SpringBoot详解实现自定义异常处理页面方法

    下面是关于“SpringBoot详解实现自定义异常处理页面方法”的完整攻略: SpringBoot详解实现自定义异常处理页面方法 前言 在我们的应用程序中,经常会遇到一些异常问题,比如资源不存在、参数错误等等,这时候我们就需要对这些异常进行统一处理,并且返回给用户友好的错误提示信息。在SpringBoot中,通过实现自定义异常处理页面方法,我们可以非常方便地…

    Java 2023年5月27日
    00
  • Java线程池由浅入深掌握到精通

    Java线程池从入门到精通 Java线程池是一种多线程处理机制,用于管理和调度多个线程。通过线程池,可以复用线程、控制线程数量,从而提高程序并发处理能力和资源利用率。 1. 初识Java线程池 1.1 线程池的优点 使用线程池具有以下优点: 降低线程创建和销毁带来的性能损耗; 通过重用线程来优化程序性能; 可以对线程数量进行限制和控制,避免系统资源被消耗殆尽…

    Java 2023年5月19日
    00
  • IntelliJ IDEA 2019如何开启自动编译?IntelliJ IDEA开启自动编译教程

    下面是IntelliJ IDEA 2019如何开启自动编译的完整攻略。 1. 打开IntelliJ IDEA设置 点击菜单栏中的“File”(文件),选择“Settings…”(设置)打开IDEA的设置面板。 2. 进入编译器设置 在设置面板左侧的选项中选择“Build, Execution, Deployment”(构建、运行和部署),然后选择“Compi…

    Java 2023年5月26日
    00
  • AOP之事务管理的两种配置方式

    对于AOP之事务管理的两种配置方式,我们可以使用以下两种方式进行配置: 一、使用标签配置事务管理 1. 在XML配置文件中声明TransactionManager代理 <!– 声明 TransactionManager bean –> <bean id="txManager" class="org.spri…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部