vue +springboot + jwt 单点登录 实例

  1. springboot 代码

    首先在entity 新建实体,新建代码
java 复制代码
package com.example.demo.demos.web.demo.entity;

import lombok.Data;

@Data
public class User {
    private String username;
    private String password;
    private String token;
}
  1. controller 中写
java 复制代码
    //定义两个初始常量登录名 这里不使用数据库认证
    private final String USERNAME = "admin";
    private final String PASSWORD = "admin";
    @GetMapping("/login")

    public User login(  User loginForm){
        if(USERNAME.equals(loginForm.getUsername()) && PASSWORD.equals(loginForm.getPassword())){

            //添加token
            loginForm.setToken(JwtUtiles.createToken());
            return loginForm;
        }
        return null;
    }

添加相关依赖

xml 复制代码
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!---  注意以下是  java1.8以上需要添加--->

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>


        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>


        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>


        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

utils 中新建 文件JwtUtiles

java 复制代码
package com.example.demo.demos.web.demo.utils;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.UUID;

public class JwtUtiles {

    private static  long time = 1000* 60 *60 *24;//token有效时间
    private static String signature = "admin";
    public static String createToken(){
        JwtBuilder jwtBuilder  = Jwts.builder();
        String jwtToken = jwtBuilder
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                .claim("username","admin")
                .claim("role","admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis()+time))
                .setId(UUID.randomUUID().toString())
                .signWith(SignatureAlgorithm.HS256,signature)
                .compact();
        return jwtToken;
    }
}

前端vue 写法:
request.js 配置

html 复制代码
import axios from 'axios'

const request = axios.create({
	baseURL: 'http://localhost:8080', 
    timeout: 50000
})

// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(config => {
    config.headers['Content-Type'] = 'application/json;charset=utf-8';
    let user=localStorage.getItem("user")?JSON.parse(localStorage.getItem("user")):{}//获取登录时存放的user对象信息
    config.headers['token'] = user.token;  // 设置请求头
    return config
}, error => {
    return Promise.reject(error)
});

// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
    response => {
        let res = response.data;
        // 如果是返回的文件
        if (response.config.responseType === 'blob') {
            return res
        }
        // 兼容服务端返回的字符串数据
        if (typeof res === 'string') {
            res = res ? JSON.parse(res) : res
        }
        return res;
    },
    error => {
        console.log('err' + error) // for debug
        return Promise.reject(error)
    }
)

export default request

login.vue 界面

html 复制代码
<template>
    <div class="login_container">
       <div class="login_box">
         <div style="margin:20px 0; text-align:center; font-size:24px" ><b>登录</b></div>
         <!-- 用户名-->
         <el-form ref="LoginFormRef" :model="loginForm" :rules="LoginFormRules" >
           <el-form-item prop="username">
             <el-input size="medium" style="margin:10px 0px;width: 300px;margin-left:25px" v-model="loginForm.username" prefix-icon="el-icon-user"></el-input>
           </el-form-item>
         <!-- 密码-->
            <el-form-item prop="password">
              <el-input size="medium" style="margin:10px 0px;width: 300px;margin-left:25px" show-password v-model="loginForm.password" prefix-icon="el-icon-lock" type="password"></el-input>
           </el-form-item>
           <div style="margin:10px 0; text-align:center">
             <el-button type="primary" size="small" @click="login" >登录</el-button>
           <!--   <el-button type="warning" size="small" @click="resetLoginForm">重置</el-button> -->
           </div> 
         </el-form> 
       </div>
     </div>
   </template>
   
   <script>
     export default {
       name:"Login",
       data() {
         return {
           loginForm: {
             username:'',
             password:''
           },
           LoginFormRules:{
             username:[
               { required: true, message: '请输入用户名', trigger: 'blur' },
             ],
             password:[
               { required: true, message: '请输入密码', trigger: 'blur' },
             ]
           }
         }
       },   

       methods:{
        login(){
            this.$refs['LoginFormRef'].validate(async (valid) => {
                if (valid) {
                  let _this = this;
                    this.request.get("http://localhost:8080/user/login",{params:this.loginForm}).then(res=>{
                      console.log(this.loginForm)
                    console.log(res)
/* 	                    if(res.code=='200'){
	                        localStorage.setItem("user",JSON.stringify(res.data));//存储用户信息到浏览器
	                        this.$router.push("/home");
	                        this.$message.success("登录成功");
	                    }else{
	                        this.$message.error(res.msg);
	                    } */
                    })
                }  
            })
       },

     }
    
    }
   </script>
   
   <style scoped>
     .login_container{
       background-color: #2b4b6b;
       height: 100%;
     }
   
     .login_box{
       width: 350px;
       height: 300px;
       background-color: #fff;
       border-radius: 3px;
       position: absolute;
       left: 50%;
       top: 50%;
       transform: translate(-50%,-50%)
     }
   </style>

登录后可观察到token已经返回

添加路由守卫

js 复制代码
import Vue from 'vue'
import VueRouter from 'vue-router'
import Manage from '../views/Manage.vue'
import Aside from '@/components/Aside.vue'
import User from '@/views/user.vue'
import Login  from '../views/Login.vue'
import axios from 'axios'
Vue.use(VueRouter)
const routes = [
  {
    path: '/',
    name: 'Manage',
    redirect: '/login',
    component: Manage,
    children:[
      {
        path: 'user',
        name: 'User',
        component: User
      },
      {
        path: 'home',
        name: 'Home',
        // route level code-splitting
        // this generates a separate chunk (about.[hash].js) for this route
        // which is lazy-loaded when the route is visited.
        component: () => import(/* webpackChunkName: "about" */ '../views/Home.vue')
      }
    ]
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  },
  {
    path: '/error',
    name: 'Error',
    component: Login
  }
]



const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

//全局路由守卫
router.beforeEach((to,from,next)=>{
  if(to.path.startsWith('/login')){
    window.localStorage.removeItem('access-admin')
    next()
  }else{
    let admin = JSON.parse(window.localStorage.getItem('access-admin'))
    console.log(admin)
    if(!admin ){
      next({path:'/login'})
      location.reload()
    }else{//检查token合法性
      let token = admin.token
      console.log(token)
      axios({
        url:"http://localhost:8080/user/checkToken",
        method:'get',
        Headers:{
          token:admin.token
        },
        params:{
          token:token
        }
      }).then((response) =>{
        console.log(response.data)
        if(!response.data ){ console.log('校验失败')
        //  next({path:'/login'})
     
       
        next({path:'/login'})
        location.reload()
        }
         
      })
      next();

    }
  }
})

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch(err => err)
}


export default router

设置到期拦截

java 复制代码
package com.example.demo.demos.web.demo.controller;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.demos.web.demo.common.Constants;
import com.example.demo.demos.web.demo.common.Result;
import com.example.demo.demos.web.demo.entity.User;
import com.example.demo.demos.web.demo.entity.UserDTO;
import com.example.demo.demos.web.demo.entity.UserEntity;
import com.example.demo.demos.web.demo.mapper.UserMapper;
import com.example.demo.demos.web.demo.service.UserService;
import com.example.demo.demos.web.demo.utils.JwtUtiles;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("user")
public class UserController {
   // @Autowired
   // private UserMapper userMapper;
    @Autowired
    public UserService userService;
/*        @GetMapping("/")
        public List<UserEntity> index(){
            return userMapper.findAll();
        }*/

    //使用mybtis-plus实现查询所有数据
    @GetMapping("/")
    public List<UserEntity> findAll(){
        return userService.list();
    }


       /* @PostMapping("/add")
        //这里做了一个单纯的添加的示例,使用的是mapper中的insert方法
        public Integer save(@RequestBody UserEntity userEntity){
                return userService.save(userEntity);
        }*/

/*        @DeleteMapping("/{id}")
        public Integer deleteById(@PathVariable Integer id){
                return  userService.deleteById(id);
        }*/

    //使用mybtis-plus实现删除
    @DeleteMapping("/{id}")
    public boolean deleteById(@PathVariable Integer id){
        return  userService.removeById(id);
    }


    @PostMapping
    //使用mybtis-plus,注意这里返回的是boolean型
    public Boolean save(@RequestBody UserEntity user) {
        return userService.saveUser(user);
    }

    //使用mybtis-plus实现批量删除
    @PostMapping("/del/batch/")
    public boolean deleteBatch(@RequestBody List<Integer> ids){
        return userService.removeByIds(ids);
    }

    //分页查询
    //接口路径user/page?pageNum=1&pageSize=10
    //RequestParam接受前台传过来的第几页,每页显示数
/*    @GetMapping("/page")
    public Map<String,Object> findPage(@RequestParam Integer pageNum,@RequestParam Integer pageSize){
        pageNum=(pageNum-1)*pageSize;
        List<UserEntity> data=userService.selectPage(pageNum,pageSize);
        Integer total=userMapper.selectTotal();
        Map<String,Object> res=new HashMap<>();
        res.put("data",data);
        res.put("total",total);
        return res;

    }*/

    //使用mybtis-plus实现根据ID查找记录
    @GetMapping("/{id}")
    public UserEntity findOne(@PathVariable Integer id){
        return userService.getById(id);
    }

    //使用mybtis-plus实现模糊查询并分页
    @GetMapping("/page")
    public IPage<UserEntity> findPage(@RequestParam Integer pageNum,
                                      @RequestParam Integer pageSize,
                                      @RequestParam(defaultValue = "") String username,
                                      @RequestParam(defaultValue = "") String nickname,
                                      @RequestParam(defaultValue = "") String address){
        IPage<UserEntity> page=new Page<>(pageNum,pageSize);
        QueryWrapper<UserEntity> queryWrapper=new QueryWrapper<>();
        queryWrapper.like("username",username);
        queryWrapper.like("nickname",nickname);
        queryWrapper.like("address",address);
        return userService.page(page,queryWrapper);
    }



/*    @PostMapping("/login")
    public Result login(@RequestBody UserDTO userDTO){
        String username=userDTO.getUsername();//先对userDTO进行是否为空的校验
        String password=userDTO.getPassword();
        //调用hutool工具中的StrUtil函数实现用户名和密码是否为空的判断
        if(StrUtil.isBlank(username) || StrUtil.isBlank(password)){
            return Result.error(Constants.CODE_400,"参数错误");
        }
        UserDTO dto=userService.login(userDTO);
        return Result.success(dto);
    }*/


    //定义两个初始常量登录名 这里不使用数据库认证
    private final String USERNAME = "admin";
    private final String PASSWORD = "admin";
    @GetMapping("/login")
    public User login(  User loginForm){
        if(USERNAME.equals(loginForm.getUsername()) && PASSWORD.equals(loginForm.getPassword())){
            //添加token
            loginForm.setToken(JwtUtiles.createToken());
            return loginForm;
        }
        return null;
    }
    @GetMapping("/checkToken")
    public Boolean checkToken(HttpServletRequest  request,@RequestParam String token){
        String token1 = token;

        return JwtUtiles.checkToken(token1);
    }

}

写解析方法

java 复制代码
package com.example.demo.demos.web.demo.utils;

import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.UUID;

public class JwtUtiles {

    private static  long time = 1000* 6;//token有效时间
    private static String signature = "admin";
    public static String createToken(){
        JwtBuilder jwtBuilder  = Jwts.builder();
        String jwtToken = jwtBuilder
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                .claim("username","admin")
                .claim("role","admin")
                .setSubject("admin-test")
                .setExpiration(new Date(System.currentTimeMillis()+time))
                .setId(UUID.randomUUID().toString())
                .signWith(SignatureAlgorithm.HS256,signature)
                .compact();
        return jwtToken;
    }

    public static boolean checkToken(String token){
        if(token == null){
            return false;
        }
        try {
            Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
        } catch (Exception e) {
            return  false;
        }
        return  true;
    }
}

login 页面

html 复制代码
<template>
    <div class="login_container">
       <div class="login_box">
         <div style="margin:20px 0; text-align:center; font-size:24px" ><b>登录</b></div>
         <!-- 用户名-->
         <el-form ref="LoginFormRef" :model="loginForm" :rules="LoginFormRules" >
           <el-form-item prop="username">
             <el-input size="medium" style="margin:10px 0px;width: 300px;margin-left:25px" v-model="loginForm.username" prefix-icon="el-icon-user"></el-input>
           </el-form-item>
         <!-- 密码-->
            <el-form-item prop="password">
              <el-input size="medium" style="margin:10px 0px;width: 300px;margin-left:25px" show-password v-model="loginForm.password" prefix-icon="el-icon-lock" type="password"></el-input>
           </el-form-item>
           <div style="margin:10px 0; text-align:center">
             <el-button type="primary" size="small" @click="login" >登录</el-button>
           <!--   <el-button type="warning" size="small" @click="resetLoginForm">重置</el-button> -->
           </div> 
         </el-form> 
       </div>
     </div>
   </template>
   
   <script>
     export default {
       name:"Login",
       data() {
         return {
           loginForm: {
             username:'',
             password:''
           },
           LoginFormRules:{
             username:[
               { required: true, message: '请输入用户名', trigger: 'blur' },
             ],
             password:[
               { required: true, message: '请输入密码', trigger: 'blur' },
             ]
           }
         }
       },   

       methods:{
        login(){
            this.$refs['LoginFormRef'].validate(async (valid) => {
                if (valid) {
                  let _this = this;
                    this.request.get("http://localhost:8080/user/login",{params:this.loginForm}).then(res=>{
                    //  console.log(res)
                      if(res != null){

                        localStorage.setItem('access-admin',JSON.stringify(res))
                        //跳转登录页
                       // this.$router.replace({path:'/home'})
                       this.$router.push("/home");
                      }
                   // console.log(res)
/* 	                    if(res.code=='200'){
	                        localStorage.setItem("user",JSON.stringify(res.data));//存储用户信息到浏览器
	                        this.$router.push("/home");
	                        this.$message.success("登录成功");
	                    }else{
	                        this.$message.error(res.msg);
	                    } */
                    })
                }  
            })
       },

     }
    
    }
   </script>
   
   <style scoped>
     .login_container{
       background-color: #2b4b6b;
       height: 100%;
     }
   
     .login_box{
       width: 350px;
       height: 300px;
       background-color: #fff;
       border-radius: 3px;
       position: absolute;
       left: 50%;
       top: 50%;
       transform: translate(-50%,-50%)
     }
   </style>
相关推荐
harrain9 小时前
什么!vue3.4开始,v-model不能用在prop上
前端·javascript·vue.js
qq_2975746714 小时前
【实战教程】SpringBoot 集成阿里云短信服务实现验证码发送
spring boot·后端·阿里云
fanruitian14 小时前
uniapp android开发 测试板本与发行版本
前端·javascript·uni-app
rayufo14 小时前
【工具】列出指定文件夹下所有的目录和文件
开发语言·前端·python
RANCE_atttackkk14 小时前
[Java]实现使用邮箱找回密码的功能
java·开发语言·前端·spring boot·intellij-idea·idea
韩立学长15 小时前
【开题答辩实录分享】以《智能大学宿舍管理系统的设计与实现》为例进行选题答辩实录分享
数据库·spring boot·后端
2501_9445255416 小时前
Flutter for OpenHarmony 个人理财管理App实战 - 支出分析页面
android·开发语言·前端·javascript·flutter
李白你好17 小时前
Burp Suite插件用于自动检测Web应用程序中的未授权访问漏洞
前端
刘一说18 小时前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
大佐不会说日语~18 小时前
使用Docker Compose 部署时网络冲突问题排查与解决
运维·网络·spring boot·docker·容器