配置MySQL与登录模块

news/2024/7/10 3:22:09 标签: mysql, 数据库, vue, springboot

 使用技术

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博客


http://www.niftyadmin.cn/n/5402751.html

相关文章

3. 文档概述(Documentation Overview)

3. 文档概述&#xff08;Documentation Overview&#xff09; 本章节简要介绍一下Spring Boot参考文档。它包含本文档其它部分的链接。 本文档的最新版本可在 docs.spring.io/spring-boot/docs/current/reference/ 上获取。 3.1 第一步&#xff08;First Steps&#xff09; …

【Python入门教程】Python实现鸡兔同笼

今天跟大家分享一下很久之前自己做的鸡兔同笼求解问题的小游戏&#xff0c;使用公式和基本的判断语句即可实现&#xff0c;可以用来当练手或者消磨时间用。 大家在编代码的时候最重要就是先理清逻辑思路&#xff0c;例如应该套几层循环、分几个模块等等。然后在编码时可以先随意…

RISC-V特权架构 - 特权模式与指令

RV32/64 特权架构 - 特权模式与指令 1 特权模式2 特权指令2.1 mret&#xff08;从机器模式返回到先前的模式&#xff09;2.2 sret&#xff08;从监管模式返回到先前的模式&#xff09;2.3 wfi&#xff08;等待中断&#xff09;2.4 sfence.vma&#xff08;内存屏障&#xff09; …

MATLAB图像噪声添加与滤波

在 MATLAB 中添加图像噪声和进行滤波通常使用以下函数&#xff1a; 添加噪声&#xff1a;可以使用imnoise函数向图像添加各种类型的噪声&#xff0c;如高斯噪声、椒盐噪声等。 滤波&#xff1a;可以使用各种滤波器对图像进行滤波处理&#xff0c;例如中值滤波、高斯滤波等。 …

vue3 vite项目一运行就401(Unauthorized)

问题&#xff1a;项目一执行&#xff1a; pnpm run dev, 启动就出错&#xff0c; Failed to load resource: the server responded with a status of 401 (Unauthorized) 分析&#xff1a; 项目之前是正常运行的&#xff0c;没有问题&#xff0c;回溯刚刚改动&#xff0c;还原…

TypeError: SnakeEnv.reset() got an unexpected keyword argument ‘seed‘

TypeError: SnakeEnv.reset() got an unexpected keyword argument ‘seed’ logger.deprecation( Traceback (most recent call last): File “H:\AILab\RL\Snaker2\snake_train.py”, line 15, in model.learn(total_timesteps10000) File “D:\Anaconda3\envs\lab\lib\sit…

xxl-job异步任务日志打印到调度器任务管理日志

文章目录 1. xxl-job-core模块添加过滤器2. 执行器logback.xml添加过滤器配置3. 测试 1. xxl-job-core模块添加过滤器 package com.xxl.job.core.config;import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.…

ChatGPT 4.0:革新文献检索与推荐体验

ChatGPT 4.0&#xff1a;革新文献检索与推荐体验 随着信息时代的到来&#xff0c;学术文献的数量急剧增加&#xff0c;如何快速而准确地检索到所需的文献&#xff0c;以及发现潜在有价值的研究&#xff0c;成为了学术界的一大挑战。ChatGPT 4.0作为最新一代的自然语言处理模型…