新建项目
新建springboot项目,内建vue模块,如图
springboot后端
导入依赖
jwt依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shrimpking</groupId>
<artifactId>springboot-63</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-63</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置
application.properties
server.port=8089
pojo
user.java
package com.shrimpking.pojo;
import lombok.Data;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/9/14 18:10
*/
@Data
public class User
{
private Integer id;
private String username;
private String password;
private String token;
}
controller
userController.java
package com.shrimpking.controller;
import com.shrimpking.pojo.User;
import com.shrimpking.utils.JwtUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/9/14 18:12
*/
@RestController
public class UserController
{
private final String USERNAME = "admin";
private final String PASSWORD = "1234";
@GetMapping("/login")
public User login(User user){
if(USERNAME.equals(user.getUsername()) && PASSWORD.equals(user.getPassword())){
//添加token
user.setToken(JwtUtil.createToken());
return user;
}
return null;
}
@GetMapping("/checkToken")
public boolean checkToken(HttpServletRequest request){
String token = request.getHeader("token");
return JwtUtil.checkToken(token);
}
}
解决跨域访问
config
package com.shrimpking.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
// 案例 一
@Configuration
public class CorsConfig implements WebMvcConfigurer
{
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
//是否发送Cookie
.allowCredentials(true)
//放行哪些原始域
.allowedOriginPatterns("*")
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
.allowedHeaders("*")
.exposedHeaders("*");
}
}
工具类
jwtutils
package com.shrimpking.utils;
import ch.qos.logback.classic.turbo.TurboFilter;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.UUID;
/**
* Created by IntelliJ IDEA.
*
* @Author : Shrimpking
* @create 2023/9/14 18:50
*/
public class JwtUtil
{
//1天
//private static long time = 1000 * 60 * 60 * 24;
private static long time = 1000 * 5;
private static String signature = "king";
public static String createToken(){
JwtBuilder jwtBuilder = Jwts.builder();
String token = jwtBuilder
//header
.setHeaderParam("typ","JWT")
.setHeaderParam("alg","HS256")
//payload
.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 token;
}
public static boolean checkToken(String token){
if(token == null){
return false;
}
try
{
//token是否能正常解析,出现异常说明token异常
JwtParser jwtParser = Jwts.parser();
Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);
}
catch (Exception e)
{
return false;
}
return true;
}
}
vue前端
配置
main.js
import Vue from 'vue'
import App from './App.vue'
import store from '@/store'
import router from "@/router";
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from "axios";
axios.defaults.baseURL='http://localhost:8089';
Vue.prototype.$http = axios;
Vue.use(ElementUI,{size:'small'});
Vue.config.productionTip = false;
new Vue({
render: h => h(App),
store,
router
}).$mount('#app');
app.vue
<template>
<div id="app">
<router-view/>
</div>
</template>
<style lang="scss">
</style>
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from "@/views/Login";
import Home from "@/views/Home";
import About from "@/views/About";
import axios from "axios";
import Error from "@/views/Error";
Vue.use(VueRouter);
const routes = [
{
path:'/',
redirect:'/login'
},
{
path:'/login',
name:'Login',
component:Login
},
{
path:'/home',
name:'Home',
component: Home
},
{
path:'/about',
name:'About',
component:About
},
{
path:'/error',
name:'Error',
component:Error
}
];
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
});
//导航守卫
router.beforeEach( (to, from, next) =>{
//前往登录的,把本地存储的user删除
if(to.path.startsWith('/login')){
window.localStorage.removeItem('access-admin');
next();
}else {
let admin = JSON.parse(window.localStorage.getItem('access-admin'));
if(!admin){
next({path:'/login'});
} else {
//校验token的合法性
axios({
url:'/checkToken',
method:'get',
headers:{
token: admin.token
}
})
.then( (res) => {
if(!res.data){
//验证失败
console.log('校验失败');
next({path:'/error'});
}
});
next();
}
}
});
export default router
views
login.vue
<template>
<div style="width: 400px;height: 300px;margin: 100px auto;
border: 1px solid gray;border-radius: 15px;padding: 20px;">
<h1 style="margin-bottom: 45px; text-align: center;">系统登录</h1>
<el-form
ref="ruleForm"
:model="ruleForm"
:rules="rules"
label-width="80px"
label-position="left">
<el-form-item label="用户名" prop="username">
<el-input v-model="ruleForm.username"></el-input>
</el-form-item>
<el-form-item label="密码" prop="password">
<el-input v-model="ruleForm.password" type="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary"
@click="onSubmit"
style="width: 200px;margin-left: 30px;margin-top: 26px;">
登录
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "Login",
data(){
return {
ruleForm:{
username:'admin',
password:'1234'
},
rules:{
username:[{required: true, message:'请输入用户名', trigger: 'blur'}],
password: [{required: true, message: '请输入密码', trigger: 'blur'}]
}
}
},
methods:{
onSubmit(){
this.$refs.ruleForm.validate( (valid) => {
if(valid){
this.$http.get('/login',{ params:this.ruleForm})
.then(res => {
console.log(res);
if(res.data !== ""){
//登录成功
localStorage.setItem("access-admin",JSON.stringify(res.data));
this.$message.success('登录成功');
this.$router.push('/home');
}else {
this.$message.error('用户名或密码错误!');
}
})
} else {
//啥都没填
console.log('error login');
return false;
}
})
}
}
}
</script>
<style scoped>
</style>
home.vue
<template>
<div>
<p>用户:{{ admin.username }}</p>
<div id="nav">
<router-link to="/about">下载</router-link>
</div>
<h1>这是首页</h1>
</div>
</template>
<script>
export default {
name: "Home",
data(){
return {
admin:''
}
},
created(){
this.admin = JSON.parse(window.localStorage.getItem('access-admin'));
}
}
</script>
<style scoped>
</style>
about.vue
<template>
<div>
<p>用户:{{ admin.username }}</p>
<div id="nav">
<router-link to="/home">返回首页</router-link>
</div>
<a href="#">资源1</a>
<a href="#">资源2</a>
<a href="#">资源3</a>
<a href="#">资源4</a>
<a href="#">资源5</a>
<a href="#">资源6</a>
</div>
</template>
<script>
export default {
name: "About",
data(){
return {
admin:''
}
},
created(){
this.admin = JSON.parse(window.localStorage.getItem('access-admin'));
}
}
</script>
<style scoped>
#nav{
margin-bottom: 30px;
}
a{
margin-left: 10px;
}
</style>
error.vue
<template>
<div>
<h1>ERROR</h1>
<h3>出错了!!!</h3>
</div>
</template>
<script>
export default {
name: "Error",
created(){
this.$alert('登录信息失效,点击确定3秒后回到登录页面','提示',{
confirmButtonText:'确定'
}).then(() => {
window.localStorage.removeItem('access-admin');
setTimeout(() =>{
this.$router.push('/login');
},3000);
})
}
}
</script>
<style scoped>
</style>