【shiro】shiro整合JWT——1.需要创建的类

前言

shiro整合JWT系列,主要记录核心思路–如何在shiro+redis整合JWTToken。
该篇主要讲述整合JWT需要创建那些类,如下:

  1. JwtToken (JWT实体类)
  2. JwtUtil (JWT工具类)
  3. JwtFilter (JWT拦截器)

所使用的依赖是:

        <!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        
        <!--JWT-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>  

		<!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>    

1、JwtToken (JWT实体类)

该类实现了AuthenticationToken中的2个接口,getPrincipal()getCredentials(),主要用来获取token。
这两个方法之前是由UsernamePasswordToken实现,用于获取用户名和密码。

  • JwtToken代码如下:
public class JwtToken implements AuthenticationToken {
	/**
     * 用于确保在对象序列化过程中,版本号一致。
     * 当一个对象被序列化后,它的字节流可以被存储在文件系统中或通过网络传输到另一个计算机。
     * 当接收方收到字节流并反序列化它时,它需要确保序列化和反序列化的版本号一致,否则就会抛出版本不一致的异常。
     * 因此,在序列化类中,需要为每个类提供一个唯一的 serialVersionUID,确保在版本升级时,反序列化仍然可以正确地工作。
     */
    private static final long serialVersionUID = 1L;
    private String token;

    public JwtToken(String token) {
        this.token = token;
    }
    
    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

疑问:这个类具体是怎么使用到的?
回答:当获取到前端传来的token字符串后,将该字符串token存入JwtToken 对象中(如:new JwtToken(token) ),当使用的时候,只需要调用上面任意方法就可以取出前端传来的token。

2、JwtUtil (JWT工具类)

JWT工具类最主要的应该就是以下3个方法:

  1. verify:验证
    目的:验证token是否正确,判断前端传入的token后端生成的token是否一样
    账号和密钥按指定Algorithm 创建出来的JWT对象,可以理解就是我们的token。

  2. getUsername:获取token中的账号(用户名)
    目的:用于查询数据库中该用户的信息(得到存于数据库中的加密密码)
    Jwt中主要包含header和payload两个部分。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. sign:生成签名
    目的:创建该用户的token
    密钥按指定Algorithm生成的实例 + payload信息 + 过期时间,生成token(还可以加更多内容,有兴趣再去研究)

  • JwtUtil代码如下:
public class JwtUtil {

    // 过期时间30分钟
    public static final long EXPIRE_TIME = 30 * 60 * 1000;

    /**
     * 1、校验token是否正确
     *
     * @param token  密钥
     * @param secret 用户的密码
     * @return 是否正确
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            // 根据密钥(这里是密码)生成一个算法实例
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 生成JWT效验器
            JWTVerifier verifier = JWT.require(algorithm) // 设置一个以该算法为基础的校验器
                                      .withClaim("username", username) // payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解
                                      .build(); // 创建校验器
            // 效验TOKEN,如果出现不匹配,就会出现异常
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 2、获得token中的信息无需secret解密也能获得
     *
     * JWT.decode解码:可以查看token中的head,payload信息
     * 如果想要校验token,则需要知道sign的算法和附带的payload信息
     * sign的算法:Algorithm.HMAC256(secret)
     * 附带的payload信息:JWT.withClaim
     * @return token中包含的用户名
     */
    public static String getUsername(String token) {
        try {
            // 对token进行解码【可以查看token中的head,payload信息】
            DecodedJWT jwt = JWT.decode(token);
            // 获取解码信息中的username
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 3、生成签名,5min后过期
     * 生成token签名EXPIRE_TIME 分钟后过期
     * @param username 用户名
     * @param secret   用户的密码【secret应该是定义的密钥,这里是用当前用户密码当作密钥】
     * @return 加密的token
     */
    public static String sign(String username, String secret) {
        try{
            //现在系统的时间 + 过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            // 根据密钥(这里是密码)生成一个算法实例
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附带username信息
            return JWT.create()
                    .withClaim("username", username) // payload 存储非敏感的信息 例如用户账号,不能存密码,防止被人解析
                    .withExpiresAt(date) // 指定令牌的过期时间
                    .sign(algorithm); // 签名  保密复杂
        }catch (UnsupportedEncodingException e){
            return null;
        }

    }

3、JwtFilter (JWT拦截器)

注意:引入JWT后,核心登录subject.login()将在JwtFilter类的executeLogin方法里。

public class JwtFilter extends BasicHttpAuthenticationFilter {

    /**
     * 执行登录认证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            executeLogin(request, response);
            return true;
        } catch (Exception e) {
            throw new AuthenticationException("Token失效请重新登录");
        }
    }

    /**
     * 执行登录
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("ACCESS_TOKEN");

        JwtToken jwtToken = new JwtToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获 
        // 这里真正实现shiro的登录,getSubject(request, response)直接SecurityUtils.getSubject()也一样
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

	/**
     * 如果没有登录,直接返回401未授权提示
     *
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 这里是个坑,如果不设置的接受的访问源,那么前端都会报跨域错误,因为这里还没到corsConfig里面
        httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) request).getHeader("Origin"));
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setCharacterEncoding("UTF-8");
        httpResponse.setContentType("application/json; charset=utf-8");
		httpResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
        try {
            // 返回到前端信息
            httpResponse.getWriter().write("未登录或登录失效,请重新登录!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected 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请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}

疑惑:我知道了token的值,在模拟请求中,这个token应该放在header里的哪个字段?
回答:这个是我们代码中定义的,String token = httpServletRequest.getHeader("ACCESS_TOKEN");这段就是获取请求header中名为ACCESS_TOKEN的值;也就是我们说token存放的地方。(注意:ACCESS_TOKEN这个名称可以自己定义)
ps:辣鸡的我就这个地方找了好久😢

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值