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日

相关文章

  • Java实用工具之StringJoiner详解

    Java实用工具之StringJoiner详解 在Java中,如果需要将多个字符串连接成一个字符串,可以使用String类中的concat方法或加号+运算符进行字符串拼接。但是当我们需要连接的字符串数量较多,或者需要在每个字符串之间添加一定的分隔符时,这两种方法就显得有些麻烦。 针对这种情况,Java提供了一个实用工具类StringJoiner,它可以轻松地…

    Java 2023年5月26日
    00
  • Spring Boot 自动配置的实现

    Spring Boot自动配置是Spring Boot的一个重要特性,它可以帮助我们快速构建应用程序,减少配置工作。以下是Spring Boot自动配置的实现的详细攻略: 自动配置原理 Spring Boot自动配置的原理是基于Spring的条件化配置机制。Spring Boot会根据应用程序的classpath、配置文件和其他条件来自动配置应用程序。如果应…

    Java 2023年5月15日
    00
  • java基础知识I/O流使用详解

    Java基础知识I/O流使用详解 1. I/O流概述 Java I/O流用于处理与设备(如磁盘、屏幕、键盘等)的输入和输出。在Java中,I/O流分为两个类型:字节流和字符流。字节流用于以字节为单位读取和写入数据,而字符流用于以字符为单位读取和写入数据。 I/O流被划分为四个抽象类:InputStream、OutputStream、Reader和Writer…

    Java 2023年5月24日
    00
  • 如何在Java中实现一个散列表

    散列表(Hash Table)是一种以键值对结构存储数据的数据结构,它可以高效地实现插入、删除和查找操作。在Java中,我们可以使用HashMap来实现一个散列表。 下面是实现一个散列表的步骤: 定义一个HashMap对象 在Java中,我们可以使用HashMap来实现散列表。因此,首先要定义一个HashMap对象。我们可以使用以下语法: HashMap&l…

    Java 2023年5月19日
    00
  • 谈谈Spring Boot 数据源加载及其多数据源简单实现(小结)

    这篇攻略解释了如何在Spring Boot中加载数据源,并提供了实现多数据源的简单示例。 一、Spring Boot加载数据源的基本原理 Spring Boot中加载数据源的方式是通过自动配置。根据应用程序的classpath路径以及类路径上的标记,Spring Boot会自动配置适当的数据源,如果没有其他配置,将选择一个默认情况下适合多数场景的数据源。 在…

    Java 2023年5月20日
    00
  • Java中Stream流去除List重复元素的方法

    首先要说明一下,Java中的Stream流是Java8中新增的一种函数式操作流程,主要用来对集合进行函数式操作,它可以对集合进行一些链式操作,比如筛选、分组、排序、去重等。 List去重,在Java8中,可以借助Stream流,具体步骤如下: 使用Stream.builder()来构造一个Stream.Builder对象; 通过builder对象调用add方…

    Java 2023年5月31日
    00
  • MyEclipse+Tomcat配置详解(图文)

    首先,需要说明的是,配置MyEclipse和Tomcat的过程并不是一成不变的,不同版本的软件可能会有些许差别。但是,总体上来说,配置过程都是大同小异的。接下来,我将根据网站上的“MyEclipse+Tomcat配置详解(图文)”文章,为大家详细讲解配置过程。 步骤一:下载MyEclipse和Tomact 要配置MyEclipse和Tomcat,自然需要先下…

    Java 2023年5月19日
    00
  • jsp学习之scriptlet的使用方法详解

    JSP学习之Scriptlet的使用方法详解 一、Scriptlet的概念 Scriptlet是一段嵌入在JSP文档中的Java代码,它用于在JSP页面中执行Java代码。 在Scriptlet中,可以定义变量、定义方法,或者调用方法等等。 二、Scriptlet的语法 JSP页面中使用Scriptlet时,需要使用<% %>标签。其中,标签中间…

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