手把手教你Shiro整合JWT实现登录认证


1.所用技术

  • SpringBoot
  • Mybatis-plus
  • Shiro
  • JWT
  • Redis

2.前置知识

Shiro:

Shiro 是一个基于 Java 的开源的安全框架。

在 Shiro 的核心架构里面,Subject 是访问系统的用户。SecurityManager 是安全管理器,负责用户的认证和授权,相当于 Shiro 的老大哥。

Realm 相当于数据源,用户的认证和授权都在 Realm 的方法中进行。

cryptography 用来管理用户的密码,对密码进行加密解密操作。

JWT:

JWT 全称 json web token,其实就是将用户的登录信息、过期时间以及加密算法经过”揉搓”之后生成的一串字符串,这个字符串又叫做令牌,当然你也可以叫做 token。

用户要想访问系统,请求头中必须携带使用 JWT 生成的 token。token 校验通过了,才能访问系统,否则抛出异常。

3.流程讲解

1.用户点击注册,系统将密码加密后存入数据库中。

图片[1] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

2.用户登录

主要是校验账号密码并生成 token。

图片[2] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

3.访问资源

其实在 Shiro 整合 JWT 的系统中,关键就是通过 JwtFilter 过滤器去校验请求头中是否包含 token,如果有 token,就交给自定义的 Realm。

然后在 Realm 的认证方法里面校验 token 是否正确、是否过期等。

图片[3] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

4.初始化 SpringBoot 项目

1.新建数据库表

CREATE TABLE `t_user` (`id` bigint NOT NULL COMMENT 'id',`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '姓名',`age` int DEFAULT NULL COMMENT '年龄',`sex` tinyint DEFAULT '0' COMMENT '性别:0-女 1-男',`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '账号',`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '密码',`created_date` datetime DEFAULT NULL COMMENT '创建时间',`updated_date` datetime DEFAULT NULL COMMENT '修改时间',`is_deleted` int DEFAULT '0' COMMENT '删除标识',PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;

图片[4] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

2.新建 SpringBoot 项目

图片[5] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

添加依赖:

org.springframework.bootspring-boot-starter-weborg.apache.shiroshiro-spring-boot-starter1.5.3com.auth0java-jwt3.10.3org.springframework.bootspring-boot-starter-data-redismysqlmysql-connector-javaruntimeorg.projectlomboklomboktruecom.baomidoumybatis-plus-boot-starter3.4.0com.baomidoumybatis-plus-generator3.5.1org.freemarkerfreemarkercom.alibabadruid1.1.6cn.hutoolhutool-all5.5.7org.springframework.bootspring-boot-starter-testtest

修改配置文件:

主要是设置数据源、mybatis-plus、redis 以及 jwt 秘钥。

server:port: 8081servlet:context-path: /shiro_jwtspring:# 数据源datasource:url: jdbc:mysql://localhost:3306/shiro_jwt" />

图片[6] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

配置 Redis(不是重点):

图片[7] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[8] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

加解密工具类:

这里主要使用了 hutool 的加解密工具方法。

图片[9] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

全局异常处理:

图片[10] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

统一返回结果:

图片[11] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

5.配置 JWT

1.JWT 工具类

主要用来生成 token、校验 token

图片[12] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

2.JWTFilter

在 shiro 中,shiroFilter 用来拦截所有请求。

但是 shiro 要和 jwt 整合,所以要使用自定义的过滤器 JwtFilter。

JwtFilter 的主要作用就是拦截请求,判断请求头中书否携带 token。如果携带,就交给 Realm 处理。

@Component@Slf4jpublic class JwtFilter extends BasicHttpAuthenticationFilter {private String errorMsg;// 过滤器拦截请求的入口方法@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {// 判断请求头是否带上“Token”HttpServletRequest httpServletRequest = (HttpServletRequest) request;String token = httpServletRequest.getHeader("Authorization");// 游客访问电商平台首页可以不用携带 tokenif (StringUtils.isEmpty(token)) {return true;}try {// 交给 myRealmSecurityUtils.getSubject().login(new JwtToken(token));return true;} catch (Exception e) {errorMsg = e.getMessage();e.printStackTrace();return false;}}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {HttpServletResponse httpServletResponse = (HttpServletResponse) response;httpServletResponse.setStatus(400);httpServletResponse.setContentType("application/json;charset=utf-8");PrintWriter out = httpServletResponse.getWriter();out.println(JSONUtil.toJsonStr(Result.fail(errorMsg)));out.flush();out.close();return false;}/** * 对跨域访问提供支持 * * @param request * @param response * @return * @throws Exception */@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;HttpServletResponse httpServletResponse = (HttpServletResponse) response;httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));// 跨域发送一个option请求if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpServletResponse.setStatus(HttpStatus.OK.value());return false;}return super.preHandle(request, response);}}

3.JwtToken

shiro 在没有和 jwt 整合之前,用户的账号密码被封装成了 UsernamePasswordToken 对象,UsernamePasswordToken 其实是 AuthenticationToken 的实现类。

这里既然要和 jwt 整合,JWTFilter 传递给 Realm 的 token 必须是 AuthenticationToken 的实现类。

图片[13] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[14] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

6.配置 Shiro

1.ShiroConfig

ShiroConfig 主要包含 2 部分:过滤器、安全管理器

过滤器:

图片[15] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

安全管理器:

图片[16] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

2.自定义 Realm

自定义 Realm 的认证方法主要用来校验 token 的合法性:

@Componentpublic class MyRealm extends AuthorizingRealm {@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate JwtUtil jwtUtil;/** * 限定这个realm只能处理JwtToken */@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof JwtToken;}/** * 授权(授权部分这里就省略了) */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// 获取到用户名,查询用户权限return null;}/** * 认证 */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){// 获取token信息String token = (String) authenticationToken.getCredentials();// 校验token:未校验通过或者已过期if (!jwtUtil.verifyToken(token) || jwtUtil.isExpire(token)) {throw new AuthenticationException("token已失效,请重新登录");}//用户信息User user = (User) redisUtil.get("token_" + token);if (null == user) {throw new UnknownAccountException("用户不存在");}SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, token, this.getName());return simpleAuthenticationInfo;}}

7.测试

1.登录

@PostMapping("/login")public Result login(@RequestParam String username, @RequestParam String password) {// 从数据库中查找用户的信息,信息正确生成tokenreturn userService.login(username, password);}复制代码

图片[17] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[18] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[19] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[20] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

2.访问资源

token 失效:

图片[21] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[22] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

token 正常:

图片[23] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

图片[24] - 手把手教你Shiro整合JWT实现登录认证 - MaxSSL

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享