使用技术
MySQL,Mybatis-plus,spring-security,jwt验证,vue
1. 配置Mysql
1.1 下载
MySQL :: Download MySQL Installer
1.2 安装
其他页面全选默认即可
1.3 配置环境变量
将C:\Program Files\MySQL\MySQL Server 8.0\bin(如果安装到了其他目录,填写相应目录的地址即可)添加到环境变量PATH中,这样就可以在任意目录的终端中执行mysql命令了。
1.4 mysql服务的关闭与启动(默认开机自动启动,如果想手动操作,可以参考如下命令)
关闭:net stop mysql80
启动:net start mysql80
1.5 mysql的常用操作连接用户名为root,密码为123456的数据库服务:mysql -uroot -p123456
show databases;:列出所有数据库
create database kob;:创建数据库
drop database kob;:删除数据库
use kob;:使用数据库kob
show tables;:列出当前数据库的所有表
create table user(id int, username varchar(100)):创建名称为user的表,表中包含id和username两个属性。
drop table user;:删除表
insert into user values(1, 'yxc');:在表中插入数据
select * from user;:查询表中所有数据
delete from user where id = 2;:删除某行数据
2. 配置SpringBoot
Maven仓库地址:https://mvnrepository.com/
MyBatis-Plus官网:MyBatis-Plus
在pom.xml文件中添加依赖:
Spring Boot Starter JDBC
Project Lombok
MySQL Connector/J
mybatis-plus-boot-starter
mybatis-plus-generator
spring-boot-starter-security
jjwt-api
jjwt-impl
jjwt-jackson
在application.properties中添加数据库配置:
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
SpringBoot中的常用模块
pojo层:将数据库中的表对应成Java中的Class
mapper层(也叫Dao层):将pojo层的class中的操作,映射成sql语句
service层:写具体的业务逻辑,组合使用mapper中的操作
controller层:负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面
3. 修改Spring Security
传统方式:session登录认证
实现security与数据库对接
实现service.impl.UserDetailsServiceImpl类,继承自UserDetailsService接口,用来接入数据库信息
实现config.SecurityConfig类,用来实现用户密码的加密存储
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
jwt可实现跨域认证,不需要在服务器端存储
加入jwt的三个依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
实现utils.JwtUtil类
为jwt工具类(实现加密信息,解析token),用来创建、解析jwt token
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;
@Component
public class JwtUtil {
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac";
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if (ttlMillis == null) {
ttlMillis = JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid)
.setSubject(subject)
.setIssuer("sg")
.setIssuedAt(now)
.signWith(signatureAlgorithm, secretKey)
.setExpiration(expDate);
}
public static SecretKey generalKey() {
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256");
}
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt)
.getBody();
}
}
实现config.filter.JwtAuthenticationTokenFilter类
用来验证jwt token,如果验证成功,则将User信息注入上下文中
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import com.kob.backend.service.impl.utils.UserDetailsImpl;
import com.kob.backend.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private UserMapper userMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
token = token.substring(7);
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException(e);
}
User user = userMapper.selectById(Integer.parseInt(userid));
if (user == null) {
throw new RuntimeException("用户名未登录");
}
UserDetailsImpl loginUser = new UserDetailsImpl(user);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}
配置config.SecurityConfig类
package com.kob.backend.config;
import com.kob.backend.config.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/user/account/token/", "/user/account/register/").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
放行登录、注册等接口
4. 编写API
将数据库中的id域变为自增
在数据库中将id列变为自增
在pojo.User类中添加注解:@TableId(type = IdType.AUTO)
实现/user/account/token/:验证用户名密码,验证成功后返回jwt token(令牌)
实现/user/account/info/:根据令牌返回用户信息
实现/user/account/register/:注册账号
5.前端登录与注册
创建页面:
在 views 目录下创建 user ,新建 UserAccountLoginView.vue 和 UserAccountRegisterView.vue
UserAccountLoginView.vue
<template>
<ContentField>
<!-- div.row>div.col-3 -->
<div class="row justify-content-center">
<div class="col-3">
<!-- 绑定默认函数,提交触发login函数,并阻止掉默认行为 -->
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<!-- 绑定username用 v-modle -->
<input v-model="username" type="text" class="form-control" id="username" aria-describedby="请输入用户名">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password"
aria-describedby="请输入密码">
</div>
<div class="error-message">
<!-- 密码错误 -->
{{ error_message }}
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
</div>
</ContentField>
</template>
<script>
import ContentField from "../../../components/ContentField.vue"
import { useStore } from "vuex";
import { ref } from 'vue';
import router from "../../../router/index"
export default {
comments: {
ContentField
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let error_message = ref('');
const login = () => {
//清空 error_message
error_message.value = "";
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log(resp);
//成功后调用回调函数,先获取用户信息,getinfo为store.user下自定义函数名
store.dispatch("getinfo", {
success() {
//登录成功,跳转主页面
router.push({ name: 'home' });
console.log(store.state.user);
}
})
},
error() {
error_message.value = "用户名或密码有错误";
}
})
}
return {
username,
password,
error_message,
login,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
div.error-message {
color: red;
}
</style>
UserAccountRegisterView.vue
<template>
<ContentField>
注册
</ContentField>
</template>
<script>
import ContentField from "../../../components/ContentField.vue"
export default {
comments: {
ContentField
}
}
</script>
<style scoped></style>
修改store--->user.js
import $ from "jquery"
export default ({
state: {
id: "",
username: "",
photo: "",
token: "",
is_login: false,
},
getters: {
},
//修改数据
mutations: {
updateUser(state, user) {
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.is_login = user.is_login;
},
updateToken(state, token) {
state.token = token;
},
//退出登录只需在前端删除token,退出登录的辅助函数
logout(state) {
state.id = "";
state.username = "";
state.photo = "";
state.token = "";
state.is_login = false;
}
},
// 修改state的辅助函数一般写在actions
actions: {
login(context, data) {
$.ajax({
url: "http://127.0.0.1:8080/user/account/token/",
type: "post",
data: {
username: data.username,
password: data.password,
},
//获取token
success(resp) {
//在LoginServiceImpl中定义的error_message,token
if (resp.error_message === "success") {
//actions调用mutations中的函数需要用commit+字符串
context.commit("updateToken", resp.token);
data.success(resp);
} else {
data.error(resp);
}
},
error(resp) {
data.error(resp);
}
});
},
getinfo(context, data) {
$.ajax({
url: "http://127.0.0.1:8080/user/account/info/",
type: "get",
headers: {
Authorization: "Bearer " + context.state.token,
},
success(resp) {
if (resp.error_message === "success") {
context.commit("updateUser", {
...resp,
is_login: true,
});
data.success(resp);
} else {
data.error(resp);
}
},
error(resp) {
data.error(resp);
}
});
},
logout(context) {
//logout(3)调用 logout(4)
context.commit("logout");
}
},
modules: {
}
})
修改rount.js
import { createRouter, createWebHistory } from 'vue-router'
import NotFound from "../views/error/NotFound"
import PkIndexView from "../views/pk/PkIndexView"
import RanklistIndexView from "../views/ranklist/RanklistIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import UserBotIndexView from "../views/user/bot/UserBotIndexView"
import UserAccountLoginView from "../views/user/account/UserAccountLoginView"
import UserAccountRegisterView from "../views/user/account/UserAccountRegisterView"
import store from '../store/index'
const routes = [
{
path: "/",
name: "home",
redirect: "/pk/",
meta: {
requestAuth: true
}
},
{
path: "/pk/",
name: "pk_index",
component: PkIndexView,
meta: {
requestAuth: true,
}
},
{
path: "/error/",
name: "404",
component: NotFound,
meta: {
requestAuth: false
}
},
{
path: "/record/",
name: "record_index",
component: RecordIndexView,
meta: {
requestAuth: true
}
},
{
path: "/ranklist/",
name: "ranklist_index",
component: RanklistIndexView,
meta: {
requestAuth: true
}
},
{
path: "/user/bot/",
name: "user_bot_index",
component: UserBotIndexView,
meta: {
requestAuth: true
}
},
{
path: "/user/account/login/",
name: "user_account_login",
component: UserAccountLoginView,
meta: {
requestAuth: false
}
},
{
path: "/user/account/register/",
name: "user_account_register",
component: UserAccountRegisterView,
meta: {
requestAuth: false
}
},
// 重定向到404
{
path: "/:catchAll(.*)",
redirect: "/error/"
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
//router在起作用之前执行的一个函数
router.beforeEach((to, from, next) => {
// 如果发现去的没有授权和登录,重定向到login,否则跳转默认页面
if (to.meta.requestAuth && store.state.is_login) {
next({ name: "user_account_login" });
} else {
next();
}
})
export default router
store--->index.js
import { createStore } from 'vuex'
import ModuleUser from './user'
export default createStore({
state: {
},
getters: {
},
mutations: {
},
actions: {
},
modules: {
user: ModuleUser,
}
})
修改components-->NavBar.vue
<!-- html -->
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<!-- 刷新 -->
<!-- <a class="navbar-brand" href="/">King Of Bots</a> -->
<!-- 点击页面不刷新用router-link -->
<router-link class="navbar-brand" :to="{ name: 'pk_index' }">King Of Bots</router-link>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<!-- active高亮 -->
<!-- <router-link class="nav-link active " :to="{ name: 'pk_index' }">对战</router-link> -->
<!-- 选中的高亮 -->
<router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'pk_index' }">对战</router-link>
</li>
<li class="nav-item">
<router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'record_index' }">对局列表</router-link>
</li>
<li class="nav-item">
<router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'ranklist_index' }">排行榜</router-link>
</li>
</ul>
<ul class="navbar-nav" v-if="$store.state.user.is_login">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
{{ $store.state.user.username }}
</a>
<ul class="dropdown-menu">
<li>
<router-link class="dropdown-item" :to="{ name: 'user_bot_index' }">my bot</router-link>
</li>
<!-- @click="logout"点击调用logout(1)函数 -->
<li><a class="dropdown-item" href="#" @click="logout">exit</a></li>
</ul>
</li>
</ul>
<ul class="navbar-nav" v-else>
<li class="nav-item ">
<router-link class="nav-link " :to="{ name: 'user_account_login' }" role="button">
登录
</router-link>
</li>
<li class="nav-item ">
<router-link class="nav-link " :to="{ name: 'user_account_register' }" role="button">
注册
</router-link>
</li>
</ul>
</div>
</div>
</nav>
</template>
<!-- js -->
<script >
// 实现选中的页面高亮
import { useRoute } from 'vue-router';
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
const route = useRoute();
let route_name = computed(() => route.name)
//触发函数logout(1) 调用logout(2)
const logout = () => {
// 调用user.js中的logout(3)
store.dispatch("logout");
}
return {
route_name,
logout
}
}
}
</script>
<!-- css -->
<!-- scoped 作用:写的css会加上一个随机字符串,使得样式不会影响组件以外的部分 -->
<style scoped></style>
前端页面授权
修改router-->index.js
import { createRouter, createWebHistory } from 'vue-router'
import NotFound from "../views/error/NotFound"
import PkIndexView from "../views/pk/PkIndexView"
import RanklistIndexView from "../views/ranklist/RanklistIndexView"
import RecordIndexView from "../views/record/RecordIndexView"
import UserBotIndexView from "../views/user/bot/UserBotIndexView"
import UserAccountLoginView from "../views/user/account/UserAccountLoginView"
import UserAccountRegisterView from "../views/user/account/UserAccountRegisterView"
import store from '../store/index'
const routes = [
{
path: "/",
name: "home",
redirect: "/pk/",
meta: {
requestAuth: true
}
},
{
path: "/pk/",
name: "pk_index",
component: PkIndexView,
meta: {
requestAuth: true
}
},
{
path: "/error/",
name: "404",
component: NotFound,
meta: {
requestAuth: false
}
},
{
path: "/record/",
name: "record_index",
component: RecordIndexView,
meta: {
requestAuth: true
}
},
{
path: "/ranklist/",
name: "ranklist_index",
component: RanklistIndexView,
meta: {
requestAuth: true
}
},
{
path: "/user/bot/",
name: "user_bot_index",
component: UserBotIndexView,
meta: {
requestAuth: true
}
},
{
path: "/user/account/login/",
name: "user_account_login",
component: UserAccountLoginView,
meta: {
requestAuth: false
}
},
{
path: "/user/account/register/",
name: "user_account_register",
component: UserAccountRegisterView,
meta: {
requestAuth: false
}
},
// 重定向到404
{
path: "/:catchAll(.*)",
redirect: "/error/"
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
//router在起作用之前执行的一个函数
router.beforeEach((to, from, next) => {
// 如果发现去的没有授权和登录,重定向到login,否则跳转默认页面
if (to.meta.requestAuth && !store.state.is_login) {
next({ name: "user_account_login" });
} else {
next();
}
})
export default router
注册页面
<template>
<ContentField>
<div class="row justify-content-center">
<div class="col-3">
<!-- 绑定默认函数,提交触发login函数,并阻止掉默认行为 -->
<form @submit.prevent="register">
<div class="mb-3">
<label for="username" class="form-label">用户名</label>
<input v-model="username" type="text" class="form-control" id="username" placeholder="请输入用户名">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password" placeholder="请输入密码">
</div>
<div class="mb-3">
<label for="confirmedPassword" class="form-label">确认密码</label>
<input v-model="confirmedPassword" type="password" class="form-control" id="confirmedPassword"
placeholder="请再次输入密码">
</div>
<div class="error-message">{{ error_message }}</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
</div>
</ContentField>
</template>
<script>
import ContentField from "../../../components/ContentField.vue"
import { ref } from 'vue'
import router from "../../../router/index";
import $ from 'jquery'
export default {
comments: {
ContentField
},
setup() {
let username = ref('');
let password = ref('');
let confirmedPassword = ref('');
let error_message = ref('');
const register = () => {
$.ajax({
url: "http://127.0.0.1:8080/user/account/register/",
//如果是修改数据库用post,只获取数据用get
type: "post",
data: {
username: username.value,
password: password.value,
confirmedPassword: confirmedPassword.value,
},
// "success" : function(resp){
// console.log(resp);
// },
//简化版,关键字中的引号可以去掉,函数可以省略简写
success(resp) {
if (resp.error_message === "suceess") {
router.push({ name: "user_account_login" });
} else {
//不成功,显示错误信息
error_message.value = resp.error_message;
}
},
"error": function (resp) {
console.log(resp);
}
})
}
return {
username,
password,
confirmedPassword,
error_message,
register,
}
}
}
</script>
<style scoped>
button {
width: 100%;
}
div.error-message {
color: red;
}
</style>
登录的持久化
UserAccountLoginView.vue
<template>
<!-- <ContentField v-if="show_content"> -->
<ContentField v-if="!$store.state.user.pulling_info">
<!-- div.row>div.col-3 -->
<div class="row justify-content-center">
<div class="col-3">
<!-- 绑定默认函数,提交触发login函数,并阻止掉默认行为 -->
<form @submit.prevent="login">
<div class="mb-3">
<label for="username" class="form-label">用户名</label> -->
<!-- 绑定username用 v-modle -->
<input v-model="username" type="text" class="form-control" id="username" placeholder="请输入用户名">
</div>
<div class="mb-3">
<label for="password" class="form-label">密码</label>
<input v-model="password" type="password" class="form-control" id="password" placeholder="请输入密码">
</div>
<div class="error-message">
<!-- 密码错误 -->
{{ error_message }}
</div>
<button type="submit" class="btn btn-primary">提交</button>
</form>
</div>
</div>
</ContentField>
</template>
<script>
import ContentField from "../../../components/ContentField.vue"
import { useStore } from "vuex";
import { ref } from 'vue';
import router from "../../../router/index"
export default {
comments: {
ContentField
},
setup() {
const store = useStore();
let username = ref('');
let password = ref('');
let error_message = ref('');
// let show_content = ref(false);
//取出token,如果不为空,调用updateToken,getinfo
//如果没有满足已经获取token,则不需要再调用登录页面,否则更新时会闪过login
const jwt_token = localStorage.getItem("jwt_token");
if (jwt_token) {
store.commit("updateToken", jwt_token);
store.dispatch("getinfo", {
success() {
router.push({ name: "home" });
store.commit("updatePullingInfo", false);
},
error() {
//如果token过期了,展示出登录页面
// show_content.value = true;
store.commit("updatePullingInfo", false);
}
})
} else {
//如果本地没有jwt_token的话也要展示出来
// show_content.value = true;
//拉取结束
store.commit("updatePullingInfo", false);
}
const login = () => {
//清空 error_message
error_message.value = "";
store.dispatch("login", {
username: username.value,
password: password.value,
success() {
// console.log(resp);
//成功后调用回调函数,先获取用户信息,getinfo为store.user下自定义函数名
store.dispatch("getinfo", {
success() {
//登录成功,跳转主页面
router.push({ name: "home" });
// 调
// console.log(store.state.user);
}
})
},
error() {
error_message.value = "用户名或密码有错误";
}
})
}
return {
username,
password,
error_message,
login,
// show_content,
}
}
}
</script>
修改store--->user.js
import $ from 'jquery'
export default {
state: {
id: "",
username: "",
photo: "",
token: "",
is_login: false,
pulling_info: true, // 是否正在从云端拉取信息
},
getters: {
},
mutations: {
updateUser(state, user) {
state.id = user.id;
state.username = user.username;
state.photo = user.photo;
state.is_login = user.is_login;
},
updateToken(state, token) {
state.token = token;
},
logout(state) {
state.id = "";
state.username = "";
state.photo = "";
state.token = "";
state.is_login = false;
},
updatePullingInfo(state, pulling_info) {
state.pulling_info = pulling_info;
}
},
actions: {
login(context, data) {
$.ajax({
url: "http://127.0.0.1:8080/user/account/token/",
type: "post",
data: {
username: data.username,
password: data.password,
},
success(resp) {
if (resp.error_message === "success") {
localStorage.setItem("jwt_token", resp.token);
context.commit("updateToken", resp.token);
data.success(resp);
} else {
data.error(resp);
}
},
error(resp) {
data.error(resp);
}
});
},
getinfo(context, data) {
$.ajax({
url: "http://127.0.0.1:8080/user/account/info/",
type: "get",
headers: {
Authorization: "Bearer " + context.state.token,
},
success(resp) {
if (resp.error_message === "success") {
context.commit("updateUser", {
...resp,
is_login: true,
});
data.success(resp);
} else {
data.error(resp);
}
},
error(resp) {
data.error(resp);
}
})
},
logout(context) {
localStorage.removeItem("jwt_token");
context.commit("logout");
}
},
modules: {
}
}
修改NavBar.vue
<!-- html -->
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<!-- 刷新 -->
<!-- <a class="navbar-brand" href="/">King Of Bots</a> -->
<!-- 点击页面不刷新用router-link -->
<router-link class="navbar-brand" :to="{ name: 'pk_index' }">King Of Bots</router-link>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<!-- active高亮 -->
<!-- <router-link class="nav-link active " :to="{ name: 'pk_index' }">对战</router-link> -->
<!-- 选中的高亮 -->
<router-link :class="route_name == 'pk_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'pk_index' }">对战</router-link>
</li>
<li class="nav-item">
<router-link :class="route_name == 'record_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'record_index' }">对局列表</router-link>
</li>
<li class="nav-item">
<router-link :class="route_name == 'ranklist_index' ? 'nav-link active' : 'nav-link'"
:to="{ name: 'ranklist_index' }">排行榜</router-link>
</li>
</ul>
<ul class="navbar-nav" v-if="$store.state.user.is_login">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
{{ $store.state.user.username }}
</a>
<ul class="dropdown-menu">
<li>
<router-link class="dropdown-item" :to="{ name: 'user_bot_index' }">my bot</router-link>
</li>
<!-- @click="logout"点击调用logout(1)函数 -->
<li><a class="dropdown-item" href="#" @click="logout">exit</a></li>
</ul>
</li>
</ul>
<!-- 没有拉取信息时再展示 -->
<ul class="navbar-nav" v-else-if="!$store.state.user.pulling_info">
<li class="nav-item ">
<router-link class="nav-link " :to="{ name: 'user_account_login' }" role="button">
登录
</router-link>
</li>
<li class="nav-item ">
<router-link class="nav-link " :to="{ name: 'user_account_register' }" role="button">
注册
</router-link>
</li>
</ul>
</div>
</div>
</nav>
</template>
<!-- js -->
<script >
// 实现选中的页面高亮
import { useRoute } from 'vue-router';
import { computed } from 'vue';
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
const route = useRoute();
let route_name = computed(() => route.name)
//触发函数logout(1) 调用logout(2)
const logout = () => {
// 调用user.js中的logout(3)
store.dispatch("logout");
}
return {
route_name,
logout
}
}
}
</script>
<!-- css -->
<!-- scoped 作用:写的css会加上一个随机字符串,使得样式不会影响组件以外的部分 -->
<style scoped></style>
项目实战——配置MySQL与Spring Security模块_springsecurity数据库mysq设计-CSDN博客