栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2021-10-03

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021-10-03

酒店信息管理项目——JWT令牌授权和登录

这个项目是本人在工作之余利用闲暇时间进行开发的,主要是为了弥补大学毕业设计的遗憾(本人并不是没有过_,只是没有做自己喜欢的毕业设计)。酒店信息管理系统是一个前后端分离项目,前端使用vue,后端使用java开发,开发完后部署在云服务器上。
废话少说直接上项目地址,http://121.43.170.105:8004
账号:black 密码:root (也可以自行注册)
如果有不明白可以看我的主页关于酒店信息管理项目系列的文章



文章目录
  • 酒店信息管理项目——JWT令牌授权和登录
  • 前言
  • 一、项目结构和技术栈的使用
  • 二、功能介绍
    • 1.JWT令牌授权和登录
  • 总结


前言

有需要代码的小伙伴私信我WX: 1208191350


一、项目结构和技术栈的使用

项目的整体结构采用的是前后端分离的形式:
后端框架的使用:spring-boot maven mybatis-plus mybatisX
后端技术栈的使用:
1.JWT令牌权限校验与授权登录
2.短信验证码登录,使用阿里云短信服务
3.swagger api接口文档生成器
4.mybatis-plus-generator 代码生成
5.七牛云图片上传服务,将图片保存到三方服务器
6.结合数据库使用spring-boot的定时调度任务
7.redis进行短信验证码的验证,和JWT令牌(Token)的激活(保活)
后台项目结构:

前端框架的使用:vue-cli3 element-ui vue-router
前端技术栈的使用:html css scss js vue axsio
前端项目结构:

二、功能介绍 1.JWT令牌授权和登录

1.1使用jwtUtil工具类:

package com.hotel.jwt;

import com.hotel.json.JsonUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.ResourceUtils;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.*;


public class JwtUtils {

    private static final String JWT_PAYLOAD_USER_KEY = "user";

    
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
        //计算过期时间
        Calendar c = Calendar.getInstance();
        c.add(Calendar.MINUTE,expire);
        
        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
                .setId(new String(base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())))
                .setExpiration(c.getTime())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    
    public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
        //计算过期时间
        Calendar c = Calendar.getInstance();
        c.add(Calendar.SECOND,expire);

        return Jwts.builder()
                .claim(JWT_PAYLOAD_USER_KEY, com.hotel.json.JsonUtils.toString(userInfo))
                .setId(new String(base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())))
                .setExpiration(c.getTime())
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }


    
    public static  Object getInfoFromToken(String token, PublicKey publicKey, Class userType) {
        //解析token
        Jws claimsJws = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);

        Claims body = claimsJws.getBody();
        String userInfoJson = body.get(JWT_PAYLOAD_USER_KEY).toString();
        return JsonUtils.toBean(userInfoJson, userType);

    }


    public static byte[] readBytes3(InputStream in) throws IOException {
        BufferedInputStream bufin = new BufferedInputStream(in);
        int buffSize = 1024;
        ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);

        byte[] temp = new byte[buffSize];
        int size = 0;
        while ((size = bufin.read(temp)) != -1) {
            out.write(temp, 0, size);
        }
        bufin.close();

        byte[] content = out.toByteArray();
        return content;
    }

    public static void main(String[] args) throws  Exception{
        //生成jwt令牌
        Map data = new HashMap(){{
            put("password","123456");
        }};

        //加载私钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(ResourceUtils.getFile("classpath:rsa.pri").getPath());

        String jwtToken = JwtUtils.generateTokenExpireInSeconds(data, privateKey, 10);

        System.out.println(jwtToken);


        //加载公钥
        PublicKey publicKey = RsaUtils.getPublicKey(ResourceUtils.getFile("classpath:rsa.pub").getPath());

        //解析令牌
        Map infoFromToken = (Map) JwtUtils.getInfoFromToken(jwtToken, publicKey, Map.class);

        System.out.println(infoFromToken.get("name"));
        System.out.println(infoFromToken.get("age"));

    }


}

1.2使用RsaUtil工具类:

package com.hotel.jwt;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.base64;

public class RsaUtils {
    private static final int DEFAULT_KEY_SIZE = 2048;

    
    public static PublicKey getPublicKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPublicKey(bytes);
    }

    
    public static PrivateKey getPrivateKey(String filename) throws Exception {
        byte[] bytes = readFile(filename);
        return getPrivateKey(bytes);
    }

    
    public static PublicKey getPublicKey(byte[] bytes) throws Exception {
        bytes = base64.getDecoder().decode(bytes);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePublic(spec);
    }

    
    public static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException,
        InvalidKeySpecException {
        bytes = base64.getDecoder().decode(bytes);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
        KeyFactory factory = KeyFactory.getInstance("RSA");
        return factory.generatePrivate(spec);
    }

    
    public static void generateKey(String publicKeyFilename, String privateKeyFilename, String
        secret, int keySize) throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        SecureRandom secureRandom = new SecureRandom(secret.getBytes());
        keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        // 获取公钥并写出
        byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
        publicKeyBytes = base64.getEncoder().encode(publicKeyBytes);
        writeFile(publicKeyFilename, publicKeyBytes);
        // 获取私钥并写出
        byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
        privateKeyBytes = base64.getEncoder().encode(privateKeyBytes);
        writeFile(privateKeyFilename, privateKeyBytes);
    }

    private static byte[] readFile(String fileName) throws Exception {
        return Files.readAllBytes(new File(fileName).toPath());
    }

    private static void writeFile(String destPath, byte[] bytes) throws IOException {
        File dest = new File(destPath);
        if (!dest.exists()) {
            dest.createNewFile();
        }
        Files.write(dest.toPath(), bytes);
    }


    public static void main(String[] args)throws  Exception {

        String publicKeyPath = "E:\key\rsa.pub";
        String privateKeyPath = "E:\key\rsa.pri";

        generateKey(publicKeyPath,privateKeyPath,"wfx",32);
    }


}```

## 2.读入数据

代码如下(示例):



```c
data = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())

1.3设置拦截器和对指定的接口添加白名单,放行静态资源和指定的api:

package com.hotel.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class SpringMVCConfig  implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    //配置拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // excludePathPatterns,不能写在一个里面可能不生效,要分开来
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("favicon.ico")
        ;//放行的服务,对这些服务设置白名单
    }
}

1.4登录成功颁发令牌(内含JWT令牌保活逻辑),将令牌返回给前端:

    @Override
    public ResultVo login(Map map) throws Exception {
        String username = (String) map.get("username");
        String password = (String) map.get("password");
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
            return new ResultVo(2000,"账号或密码为空");
        // 查询数据库是否存在对应的用户名和密码,注册的时候会进行判断,如果用户名存在就不让注册
        // 注册的时候用户名保证了唯一
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("user_name",username);
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) return new ResultVo(2000,"账号不存在");
        // 解密
        boolean matches = bCryptPasswordEncoder.matches(password, user.getPassword());
        if (!matches)  return new ResultVo(2000,"账户名或者密码错误");
        // user存在,登录成功
        // 颁发JWT令牌
        user.setPassword("");
        PrivateKey privateKey = null;
        try {
            ClassPathResource resource = new ClassPathResource("rsa.pri");
            InputStream inputStream = resource.getInputStream();
            privateKey = RsaUtils.getPrivateKey(JwtUtils.readBytes3(inputStream));
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 把user信息存储到token,设置token过期时间为一天
        // 并且把 user信息带给前端
        String jwtToken = JwtUtils.generateTokenExpireInMinutes(user, privateKey, 24 * 60);
        // 登录成功之后将token放到redis里面,并设置过期时间为30分钟
        // 后面对jwt进行验证,先拿到请求头的token和redis里面的token
        // 1.判断redis里面的token和请求头的token存在与否,不存在判定为token过期,返回过期信息前端重新登录
        // 2.redis的token存在,判断token与请求头的token相等,如果不想等,则为token错误
        // 3.如果相等则为token信息正确,去解析redis里面的token,并且将redis里面的token的过期时间重置为30分钟
        // 实现思路,通过redis缓存可以设置时间和重置时间,来达到发送正确的请求token保活的效果
        redisUtil.set("token",jwtToken,30 * 60);
        return new ResultVo(1000,user,jwtToken);
    }

1.5前端通过请求头携带JWT令牌给后端,后端解析令领票的正确性:
前端添加请求拦截器,给每一个请求都加上一个token请求头

package com.hotel.interceptor;

import com.hotel.IUserService;
import com.hotel.json.JsonUtils;
import com.hotel.redis.RedisUtil;
import com.hotel.vo.ResultVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;



@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private IUserService iUserService;

    @Resource
    private RedisUtil redisUtil;

    // 开启ThreadLocal
    private static ThreadLocal local = new ThreadLocal<>();

    //全局的验证功能,拦截器拦截请求
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //放行请求方法为OPTIONS的请求
        String method = request.getMethod();
        if(method.equals("OPTIONS")){
            return  true;
        }

        String headerToken = request.getHeader("token");
        String redisToken = (String) redisUtil.get("token");
        // 判断redis的token是否为空redisToken,为空则过期
        // 判断header的token是否为空,为空则是没有权限的请求,需要重新登录获取token
        if (StringUtils.isEmpty(redisToken) || StringUtils.isEmpty(headerToken)) {
            //响应客户端
            response(response);
            return false;
        }
        // 判定redisToken和headerToken是否相同,不相同不让登录(疑似换token越过验证)
        if (!redisToken.equals(headerToken)) {
            //响应客户端
            response(response);
            return false;
        }

        //校验令牌
        ResultVo result = iUserService.verify(redisToken);
        // 获取用户Id
        String userId = result.getData().toString();

        if(result.getCode() == 1000){
            // 把userId放到线程池
            local.set(userId);
            // 重新设置令牌的有效时间(token保持活性,重置为30分钟)
            redisUtil.expire("token",30 * 60);
            return true ; // 放行
        }else{
            // token错误
            response(response);
            return false;
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        local.remove();
    }

    private void response(HttpServletResponse response) throws Exception{
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();

        //响应数据
        ResultVo result = new ResultVo(2000,"令牌无效");

        String resultStr = JsonUtils.toString(result);

        writer.write(resultStr);
        writer.close();
    }

    // 请求被拦截,解析tokenThreadLocal里面放入userId
    // 请求之间可以跨域上下文使用userId,调用getUserId()
    // 请求结束之后,userId会被删除
    public static String getUserId() {
        return local.get();
    }
}
总结

以上就是今天要讲的内容,本文仅仅简单介绍了酒店管理系统如何使用JWT授权的过程。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/293021.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号