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日

相关文章

  • Spring中校验器(Validator)的深入讲解

    下面是Spring中校验器(Validator)的深入讲解的完整攻略: 什么是校验器? 校验器是Spring框架中用于对入参进行校验的机制,也是一种对提交表单或者JSON数据做前端校验的技术。 校验器的作用 校验器能够帮助我们对参数进行格式和业务上的校验,避免一些无效的操作,提高了操作的正确性和安全性。 校验器的使用 1. 自定义校验器 使用校验器需要按照S…

    Java 2023年5月19日
    00
  • Java线程池的作用是什么?

    “Java线程池的作用是什么?”是一个常见的问题,对于Java程序员而言,使用线程池可以提高程序的性能和响应速度,这是一个必备技能。本文将为你详细讲解Java线程池的作用和使用攻略。 Java线程池的作用 Java线程池的作用包括如下几点: 减少线程创建和销毁的开销 我们都知道,线程的创建和销毁是非常消耗资源的过程。如果我们每次需要处理任务时都新建一个线程来…

    Java 2023年5月11日
    00
  • 情人节写给女朋友Java Swing代码程序

    下面是详细的“情人节写给女朋友Java Swing代码程序”的攻略: 1. 确定编写目的 首先需要明确编写这个代码的目的是什么,是为了送给女朋友一份特殊的礼物,还是仅仅练习一些Java Swing编程技巧。这个目的确定好以后,就可以开始进入下一步。 2. 设计程序界面 Java Swing是一种操作系统无关的图形界面工具包,可以方便地实现各种界面。在这一步中…

    Java 2023年5月23日
    00
  • java 避免出现NullPointerException(空指针)的方法总结

    Java 避免出现 NullPointerException 的方法总结 在使用 Java 编程的过程中,我们经常会遇到空指针异常(NullPointerException),这是一种非常常见的运行时异常。下面我们来总结一下如何有效地避免空指针异常。 1. 使用 Optional 类型 Java 8 引入了 Optional 类型,可以有效地避免空指针异常。…

    Java 2023年5月27日
    00
  • Java实现的程序员老黄历实例

    Java实现的程序员老黄历是一种有趣而又实用的程序,可以帮助程序员们预知未来,并给出一些建议,让程序员收获更多的好运。下面我们一步步来讲解这个程序的实现过程。 实现步骤 首先确定本程序需要实现的功能,以及需要用到的库。本程序需要实现的功能是:根据用户输入的姓名,出生年月日,得到用户的农历生日,并给出一些有趣的建议。程序需要用到的库有: java.util.S…

    Java 2023年5月20日
    00
  • Android发送GET与POST请求的DEMO详解

    下面我将为你详细讲解“Android发送GET与POST请求的DEMO详解”这个主题,包括以下几个方面的内容: 什么是HTTP请求 Android中发送HTTP请求的方式 完整示例:Android发送GET请求 完整示例:Android发送POST请求 什么是HTTP请求 HTTP(HyperText Transfer Protocol)是一种用于传输数据的…

    Java 2023年6月15日
    00
  • Java中关于String StringBuffer StringBuilder特性深度解析

    Java中关于String StringBuffer StringBuilder特性深度解析 Java中有三种处理字符串的方式,分别是使用String、StringBuffer和StringBuilder类。这三种类在处理字符串时具有不同的特点和性能表现,下面将详细解析每个类的特性。 String类 String是Java中最常用的字符串处理类,它是一个不可…

    Java 2023年5月20日
    00
  • JDK8环境中使用struts2的步骤详解

    首先需要确认使用的操作系统已经安装了JDK8。接下来进入正式操作步骤: 下载Struts2 从官网(https://struts.apache.org/download.cgi)下载Struts2的压缩包,并解压到一个目录中。 环境变量配置 在环境变量中添加Struts2的路径,将struts2的lib目录下所有的jar包添加到CLASSPATH中。 创建项…

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