Learn Springboot & Vue 3 用户信息 集成JWT

142 阅读2分钟

个人信息修改

添加一个Person.vue表示修改信息界面

<template>
<el-card style="width: 400px">
  <el-form label-width="80px" size="small">
    <el-form-item label="用户名" >
      <el-input v-model="form.username" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="昵称" >
      <el-input v-model="form.nickname" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="邮箱">
      <el-input v-model="form.email" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="电话">
      <el-input v-model="form.phone" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item label="地址">
      <el-input v-model="form.address" autocomplete="off"></el-input>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" @click="save">确 定</el-button>
    </el-form-item>
  </el-form>
</el-card>
</template>

<script>
export default {
  name: "Person",
  data(){
    return {
      form:{},
      user: localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : {}
    }
  },
  created() {
    this.request.get('/user/username/' + this.user.username).then( res => {
      if (res.code === '200'){
        this.form = res.data
      }
    })
  },
  methods:{
    save(){
      this.request.post("/user",this.form).then(res => {
        if (res){
          this.$message.success("保存成功!")
        }else {
          this.$message.error("error!")
        }
      })
    },
  }
}
</script>

<style scoped>

</style>

同样,我们需要使用到localStorage中的user对象

我们需要先在表单中内置好本来就已有的数据

所以created一个加载信息的方法

created() {
  this.request.get('/user/username/' + this.user.username).then( res => {
    if (res.code === '200'){
      this.form = res.data
    }
  })
},

点击确定时,需要向后端发送post请求:

save(){
  this.request.post("/user",this.form).then(res => {
    if (res){
      this.$message.success("保存成功!")
    }else {
      this.$message.error("error!")
    }
  })
},

后端添加这两个方法需要请求的后端参数

@GetMapping("/username/{username}")
public Result findOne(@PathVariable String username){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("username", username);
    return Result.success(userService.getOne(wrapper));
}
@PostMapping
@ResponseBody
public boolean save(@RequestBody User user){
    return userService.saveUser(user);
}

其实get个人信息最好是使用userId作为获取参数..毕竟id是唯一

集成JWT

在pom中添加如下依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>4.2.1</version>
</dependency>

在dto中添加token字段

package com.example.springweb.contoller.dto;

import lombok.Data;

/**
 * 接收前端登录请求的参数
 */
@Data
public class UserDTO {
    private String username;
    private String password;

    private String nickname;

    private String avatarUrl;

    private String token;
}

添加utils.TokenUtils

在这里可以将password作为sign来生成token

package com.example.springweb.utils;

import cn.hutool.core.date.DateUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;

public class TokenUtils {
    /**
     * 生成Token
     * @param userId
     * @return
     */
    public static String genToken(String userId, String sign){
        return JWT.create().withAudience(userId) //将userId保存到token中作为载荷
                .withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //2h后token过期
                .sign(Algorithm.HMAC256(sign));
    }
}

在service层中设置token

public UserDTO login(UserDTO userDTO) {

    User one=getUserInfo(userDTO);

    if (one != null){
        BeanUtil.copyProperties(one, userDTO, true);
        //设置token
        String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
        userDTO.setToken(token);
        return userDTO;
    } else {
        throw new ServiceException(Constants.CODE_600, "用户名或密码错误");
    }

}

在前端request中放开token

// request 拦截器
request.interceptors.request.use( config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';

    config.headers['token'] = user.token;
    return config
}, error => {
    return Promise.reject(error)
});

同时加入user本地存储数据

// request 拦截器
request.interceptors.request.use( config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';

    let user = localStorage.getItem("user") ? JSON.parse(localStorage.getItem("user")) : null
    if (user){
        config.headers['token'] = user.token;
    }

    return config
}, error => {
    return Promise.reject(error)
});

之后在本地时就能看到自己的token

image.png

JWT拦截器

固定写法,照抄就行

config中新建Interceptor.JwtInterceptor

package com.example.springweb.common.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.auth0.jwt.exceptions.JWTVerificationException;
import com.example.springweb.common.Constants;
import com.example.springweb.entity.User;
import com.example.springweb.exception.ServiceException;
import com.example.springweb.service.UserService;
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;

public class JwtInterceptor implements HandlerInterceptor {

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");

        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        // 执行认证
        if (StrUtil.isBlank(token)) {
            throw new ServiceException(Constants.CODE_401, "无token, 请重新登录!");
        }

        String userId;
        try {
            userId = JWT.decode(token).getAudience().get(0);
        } catch (JWTDecodeException j){
            throw new ServiceException(Constants.CODE_401, "token验证失败");
        }

        //根据token中的userId查询数据库
        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);
        } catch (JWTVerificationException e){
            throw new ServiceException(Constants.CODE_401, "token验证失败, 请重新登录!");
        }

        return true;
    }
}

在config中配置拦截器

只允许使用loginregister方法

package com.example.springweb.config;

import com.example.springweb.config.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
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 InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login", "/user/register", "/**/import", "/**/export"); // 拦截所有请求,通过判断token是否合法来决定是否需要登录
    }

    @Bean
    public JwtInterceptor jwtInterceptor(){
        return new JwtInterceptor();
    }
}

在前端request.js中添加

    // 当权限验证不通过的时候给出提示
    if (res.code === '401') {
        ElementUI.Message({
            message:res.msg,
            type : 'error',
        })
    }

    return res;
},

然后就实现JWT认证了..

感觉今年S赛去不去都无所谓了啊,洗澡去咯