springboot JWT + 简单登录接口设计(青戈版)

JWT全称是JSON Web Token,如果从字面上理解感觉是基于JSON格式用于网络传输的令牌。

流程上大概是这样的:

  • 用户使用用户名密码来请求服务器
  • 服务器进行验证用户的信息
  • 服务器通过验证发送给用户一个token
  • 客户端存储token,并在每次请求时带上这个token值
  • 服务端验证token值,并返回数据

案例

导入依赖

 <!-- JWT -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.3</version>
        </dependency>


        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-core</artifactId>
            <version>5.8.15</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.11</version>
        </dependency>

编写一个TokenUtils类

在这里插入图片描述
代码如下:

package com.hyq.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.hyq.pojo.User;
import com.hyq.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Component
public class TokenUtils {

    private static IUserService staticUserService;

    @Autowired
    private IUserService userService;
        /*
        * @PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。
        * PostConstruct在构造函数之后执行,init()方法之前执行。
        * */

    @PostConstruct
    private void setUserService() {
        staticUserService = userService;
    }


    /**
     * 生成Token
     * @param userId
     * @param sign
     * @return
     */
    public static String genToken(String userId, String sign) {
        // import com.auth0.jwt.JWT;
        return JWT.create().withAudience(userId)  //将user ID保存到 token 里面 作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2小时后token  过期
                .sign(Algorithm.HMAC256(sign));    //以 sign(password) 作为 token 的密钥
    }


    /**
     * 获取当前用户信息
     * @return
     */
    public static User getCurrentUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            if (StrUtil.isNotBlank(token)) {
                String userId = JWT.decode(token).getAudience().get(0);
                return staticUserService.getById(Integer.valueOf(userId));
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

}

编写一个JwtInterceptor类(用来写拦截逻辑)

package com.hyq.config.Interceptor;

import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.hyq.common.Constants;
import com.hyq.exception.ServiceException;
import com.hyq.pojo.User;
import com.hyq.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token"); //从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        //执行认证
        if(StrUtil.isBlank(token)) {
            throw new ServiceException(Constants.CODE_401, "无token,请重登录");
        }
        // 获取 token 中 的数据
        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        }catch (JWTDecodeException j) {
            throw new ServiceException(Constants.CODE_401, "token验证失败");
        }
        //验证用户是否存在
        User user = userService.getById(userId);
        if(user == null) {
            throw new ServiceException(Constants.CODE_401, "用户不存在,请重新登录");
        }
        // 用户密码加签验证 token
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try{
            jwtVerifier.verify(token); //验证 token
        }catch (JWTDecodeException e) {
            throw new ServiceException(Constants.CODE_401, "用户不存在,请重新登录");
        }
        return true;
    }
}

编写一个WebConfig类(用来拦截请求这里除了login接口以外都拦截)

package com.hyq.config;



import com.hyq.config.Interceptor.JwtInterceptor;
import lombok.extern.slf4j.Slf4j;
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;

@Slf4j
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(jwtInterceptor)  //JwtInterceptor 实现了 HandlerInterceptor
                    .addPathPatterns("/**")
                    .excludePathPatterns("/login");
    }
}

在这里插入图片描述
现在就可以开始使用了,先试试利用postman请求一下以/login开头以外的其他接口如下表示成功:
在这里插入图片描述

我们利用一个login接口试试生成token:

service层接口代码:

package com.hyq.service;

import com.hyq.common.Result;
import com.hyq.pojo.User;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hyq.pojo.dto.LoginDTO;
import com.hyq.pojo.request.LoginRequest;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author hyq
 * @since 2023-03-24
 */
public interface IUserService extends IService<User> {


    LoginDTO login(LoginRequest request);
}

service层代码

package com.hyq.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hyq.common.Constants;
import com.hyq.exception.ServiceException;
import com.hyq.mapper.UserMapper;
import com.hyq.pojo.User;
import com.hyq.pojo.dto.LoginDTO;
import com.hyq.pojo.request.LoginRequest;
import com.hyq.service.IUserService;
import com.hyq.utils.TokenUtils;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author hyq
 * @since 2023-03-24
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {


    @Override
    public LoginDTO login(LoginRequest request) {
        User one = getUserInfo(request);
        if (one != null) {
            LoginDTO loginDTO = new LoginDTO();
            BeanUtil.copyProperties(one, loginDTO, true);
            //设置 token
            String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
            loginDTO.setToken(token);
            return loginDTO;
        } else {
            throw new ServiceException(Constants.CODE_600, "用户名或密码错误");
        }
    }

    private User getUserInfo(LoginRequest request){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", request.getUsername());
        queryWrapper.eq("password", request.getPassword());
        User one;  // 这里不能赋值为null 后面try会直接抛异常
        try {
            one = getOne(queryWrapper);
        } catch (Exception e) {
            throw new ServiceException(Constants.CODE_500, "系统错误");
        }
        return one;
    }

}

controller层代码(这里封装了一个结果集,可以去我的博文里看一下,点击这里:springboot统一封装结果集

package com.hyq.controller;

import com.hyq.common.Constants;
import com.hyq.common.Result;
import com.hyq.pojo.dto.LoginDTO;
import com.hyq.pojo.request.LoginRequest;
import com.hyq.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("login")
public class LoginController {

    @Autowired
    private IUserService userService;

    @PostMapping
    public Result login(@RequestBody LoginRequest request) {
        LoginDTO loginDTO = userService.login(request);
        if(loginDTO == null) {
            return Result.error(Constants.CODE_400,"用户名或密码错误");
        }
        return Result.success(loginDTO);
    }

}

测试(成功返回token):
在这里插入图片描述

我们把token带上试试查询功能(注意查功能提前写好了,这里不演示了)
在这里插入图片描述
我们把生成的token带上之后可以看到查出了结果;

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Aholic 冲冲冲

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

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

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

打赏作者

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

抵扣说明:

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

余额充值