【Web开发技术】JWT令牌技术(信息安全)

文章介绍了JWT作为提升跨域安全性的解决方案,详细阐述了JWT的依赖、配置、生成与解析过程,并展示了如何在Java环境中使用JWT,包括创建拦截器进行令牌校验,以及在实际的Controller层和前端Vue应用中的应用。
摘要由CSDN通过智能技术生成

一、描述

说到JWT令牌技术,就需要提到cookie和session两种技术。这两种技术在跨域问题(计算机网络的知识,百度可以搜到,就回归重点)上存在一定的局限性,跟不上流行框架和新编程思想的脚步,自然而然就需要迎来进步。JWT令牌提升了用户信息在跨域上的安全性和独立性。
如果进行接口测试,建议是提前关闭令牌,这样就方便白盒测试,避免不必要的时间消耗。

在这里插入图片描述

二、依赖

<!-- jwt包 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt}</version>
</dependency>
<!-- 简化代码 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

三、配置

关于application.yml全局配置文件

# xpq是我的名字缩写,你可以改成你自己的。但得注意下面的文件中都得改
xpq:
  jwt:
    # 设置jwt签名加密时使用的秘钥
    admin-secret-key: itcast
    # 设置jwt过期时间
    admin-ttl: 7200000
    # 设置前端传递过来的令牌名称
    admin-token-name: token

四、java文件中的准备

令牌特性,用于标识令牌绑定的属性,可以是用户ID,也可以是手机号码。
作用:返回令牌知道ID,保证了用户ID的安全性

public class JwtClaimsConstant {

    public static final String EMP_ID = "empId";
    public static final String USER_ID = "userId";
    public static final String PHONE = "phone";
    public static final String USERNAME = "username";
    public static final String NAME = "name";

}

创建一个JwtProperties.java,用于配置文件Bean属性定义绑定

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "xpq.jwt")
@Data
public class JwtProperties {

    /**
     * 管理端员工生成jwt令牌相关配置
     */
    private String adminSecretKey;
    private long adminTtl;
    private String adminTokenName;

    /**
     * 用户端微信用户生成jwt令牌相关配置
     */
    private String userSecretKey;
    private long userTtl;
    private String userTokenName;

}

创建一个公共属性JwtUtil.java

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;

public class JwtUtil {

    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定秘钥
     *
     * @param secretKey jwt秘钥
     * @param ttlMillis jwt过期时间(毫秒)
     * @param claims    设置的信息
     * @return
     */
    public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long expMillis = System.currentTimeMillis() + ttlMillis;
        Date exp = new Date(expMillis);

        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置过期时间
                .setExpiration(exp);

        return builder.compact();
    }

    /**
     * Token解密
     * @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token     加密后的token
     * @return
     */
    public static Claims parseJWT(String secretKey, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

}

定义一个JWT令牌校验的拦截器(JwtTokenAdminInterceptor.java)用于拦截指定Controller

@Component
@Slf4j
public class JwtTokenAdminInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 校验jwt
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法,直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(jwtProperties.getAdminTokenName());

        //2、校验令牌
        try {
            log.info("jwt令牌校验:{}", token);
            Claims claims = JwtUtil.parseJWT(jwtProperties.getAdminSecretKey(), token);
            Long empId = Long.valueOf(claims.get(JwtClaimsConstant.EMP_ID).toString());
            log.info("当前用户id:{}", empId);
            // 放入ThreadLoad中
            BaseContext.setCurrentId(empId);
            //3、通过,放行
            return true;
        } catch (Exception ex) {
            //4、不通过,响应401状态码
            response.setStatus(401);
            return false;
        }
    }
}

注册自定义的拦截器组件(WebMvcConfiguration.java)

@Configuration
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

    @Resource
    private JwtTokenAdminInterceptor jwtTokenAdminInterceptor;

	/**
     * 注册自定义拦截器
     * @param registry
     */
    protected void addInterceptors(InterceptorRegistry registry) {
        log.info("开始注册自定义拦截器...");
        registry.addInterceptor(jwtTokenAdminInterceptor)
                .addPathPatterns("/xpq/**")
                // 开放以下接口,不需要进行jwt令牌验证
                // 所以就需要在这些接口下向用户提供jwt令牌
                // 普通登录
                .excludePathPatterns("/xpq/user/login")
                // 二维码登录
                .excludePathPatterns("/xpq/qr/*");
    }
	
}

五、开始使用

Controller层。 用户之后的所有同Controller or 同根访问路径操作都需要此token令牌才能进行。确保了用户的数据安全性独立性,也保证了服务器资源的合理利用。

@RestController
@RequestMapping("/xpq/user")
@Slf4j
public class UserController {

    @Resource
    private JwtProperties jwtProperties;

	@PostMapping("/login")
    @ApiOperation("用户登录")
    public Result<UserLoginVO> login(User user) {
        log.info("用户登录(邮箱):{}", user.getEmail());

		// 登录逻辑 throw new Exception("多种错误")
        // User u = userService.login(user);

        // 登录成功后,生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, u.getId());
        String token = JwtUtil.createJWT(
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);
        log.info("jwt令牌:{}", token);

		// 返回给用户的数据,token密匙
        UserLoginVO userLoginVO = UserLoginVO.builder()
                .token(token)
                .build();

        return Result.success(userLoginVO);
    }
}

用户端得到如下数据

在这里插入图片描述

服务器返回如下

在这里插入图片描述

前端配置(基于Vue框架axios方法的使用)

  • 1、普通用法

挂起axios全局操作,需要配置main.js

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import axios from 'axios'

const app = createApp(App)

app.config.globalProperties.$http = axios
app.use(store).use(router)
app.mount('#app')

接收到jwt令牌

// 存储jwt令牌
sessionStorage.setItem('jwt', res.token)

具体连接下再定义headers

// 使用令牌,最好是在App.vue这种最上层的Vue里使用,之后就无需配置
this.$http.defaults.headers.common['token'] = sessionStorage.getItem('jwt')
  • 2、api.js全局访问文件

直接配置api文件,只要在此文件下定义访问方法,可以一劳永逸。

import axios from 'axios'
import axiosExtra from 'axios-extra'

// 服务器端口
const baseUrl = 'http://localhost:8010'

const http = axios.create({
    baseURL: baseUrl,
    // 配置令牌
    headers: { 'token': sessionStorage.getItem('jwt') }
})

const httpExtra = axiosExtra.create({
    maxConcurrent: 5, //并发为1
    queueOptions: {
        retry: 3, //请求失败时,最多会重试3次
        retryIsJump: false //是否立即重试, 否则将在请求队列尾部插入重试请求
    }
})

http.interceptors.response.use(response => {
    return response.data
})

/**
 * 获取用户文件信息
 * @param {*} url 
 * @returns 
 */
const queryFileInfo = (url) => {
    return http.post('/xpq/file/page', url)
}

export {
    queryFileInfo,
    httpExtra
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云端new守夜人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值