配置MySQL与登录模块

 使用技术

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/422894.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

日志到filebeat-->logstash-->elastic-->kibana

1、日志到filebeat。 cat /etc/filebeat/filebeat.yml filebeat.inputs: - type: syslog format: rfc3164 protocol.udp: host: "0.0.0.0:514" output.logstash: hosts: ["localhost:5044"] 验证方式: tcpdump -i 网卡名称 udp port 514 2、…

three.js 点乘判断平行向量方向异同

效果&#xff1a; 代码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs"></div><div>判断的前提是两个向量平行<el-button click"judge"…

基于zemax的激光合束过程分析

系统里的透镜包括FAC/SAC及球面聚焦镜都是采用市面上标准的透镜&#xff0c;在典型的光纤耦合14针蝶形封装中&#xff0c;最多需要三个独立的透镜才能提供有效且稳定的耦合。大多数高端激光二极管使用两个交叉的柱面方形微透镜来补偿激光二极管快轴和慢轴的发散角之间的差异。第…

顶易海关数据怎么做获客?功能详解看这里!

顶易海关数据怎么做获客呢&#xff1f;详解看这里&#xff01; 海关数据系统登录&#xff1a;hg.smtso.com/?iEF6DCB 如果对开发国外优质客户感兴趣的话&#xff0c;关注Felicia外贸说&#xff0c;一键开发客户不是问题。 海关数据主要功能&#xff1a; 报关单详情查询&#…

【监督学习之支持向量机(SVM)】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱基本原理支持向量线性SVM与非线性SVM优化问题软间隔与正则化SVM的应用实现 简述概要 了解监督学习-支持向量机&#xff08;SVM&#xff09; 知识图谱 支持向量机&#xff08;Support Vector Machine&…

【前端素材】推荐优质后台管理系统网页Star admin平台模板(附源码)

一、需求分析 1、系统定义 后台管理系统是一种用于管理和控制网站、应用程序或系统的管理界面。它通常被设计用来让网站或应用程序的管理员或运营人员管理内容、用户、数据以及其他相关功能。后台管理系统是一种用于管理网站、应用程序或系统的工具&#xff0c;通常由管理员使…

灯塔:CSS笔记

CSS&#xff1a;层叠样式表 所谓层叠 即叠加的意思&#xff0c;表示样式可以一层一层的层叠覆盖 css写在style标签中&#xff0c;style标签一般写在head标签里面&#xff0c;title标签下面 <!DOCTYPE html> <html lang"en"> <head><meta cha…

uniapp的h5端在线预览文件

步骤如下&#xff1a; 1、下载需要准备的工具文件包 2、将其解压到/static/pdf文件夹下,如图&#xff1a; 3、创建在线查看文件的页面&#xff1a; <template><view><web-view :src"path"></web-view></view> </template>&l…

程序员是如何看待“祖传代码”的?

目录 ​编辑 程序员是如何看待“祖传代码”的&#xff1f; 一、什么是“祖传代码”&#xff1f; 二、“祖传代码”的利弊 1. 可以节省开发成本 2. 可能引入安全隐患 3. 可能增加系统的维护难度 三、祖传代对程序员的影响 1. 丰富程序员的技能和知识 2. 提高程序员的创…

腾讯云学生云服务器_学生云主机_学生云数据库_云+校园特惠套餐

2024年腾讯云学生服务器优惠活动「云校园」&#xff0c;学生服务器优惠价格&#xff1a;轻量应用服务器2核2G学生价30元3个月、58元6个月、112元一年&#xff0c;轻量应用服务器4核8G配置191.1元3个月、352.8元6个月、646.8元一年&#xff0c;CVM云服务器2核4G配置842.4元一年&…

靶机渗透之My File Server: 1

Name: My File Server: 1Date release: 21 Feb 2020Author: Akanksha Sachin VermaSeries: My File ServerDownload: https://drive.google.com/uc?id1w0grAomPuFaIohBcUwDiI3QIi4fj4kje&exportdownload 对于vulnhub中的靶机&#xff0c;我们都需先下载镜像&#xff0c;然…

java的JDK选择和在win11的安装与配置

一.背景 还是公司安排的师带徒的任务。自己也回头看看。 二.JDK的选择 开发的版本java1.8。所以准备使用JDK8。 1.JDK有谁在给我们提供呢&#xff1f; 我以前知道的是sun、RedHat、Oracle、IBM。 我以前一般都是去sun的网站下载&#xff0c;后来被Oracle收购后去的Oracle…

2024年腾讯云优惠政策_腾讯云TOP10优惠活动

腾讯云服务器多少钱一年&#xff1f;62元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器218元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…

2024年腾讯云优惠券_代金券_云服务器折扣券免费领取链接

腾讯云优惠代金券领取入口共三个渠道&#xff0c;腾讯云新用户和老用户均可领取8888元代金券&#xff0c;可用于云服务器等产品购买、续费和升级使用&#xff0c;阿腾云atengyun.com整理腾讯云优惠券&#xff08;代金券&#xff09;领取入口、代金券查询、优惠券兑换码使用方法…

【中国 Elixir 开发者值得关注的 Elixir 相关动态发布网站】ElixirStatus

Elixir 是一个基于Erlang 虚拟机的函数式、面向并行的通用编程语言。如果你是一个 Elixir 开发者&#xff0c;想关注 Elixir 相关的项目动态和博客文章&#xff0c;https://elixirstatus.com/ 是一个不错的选择&#xff0c;基本每天 Elixir 相关项目开发者提交动态到该网站。

USB - Linux Kernel Menuconfig

Linux kernel&#xff0c;make menuconfig&#xff0c;和USB相关的&#xff0c;在主菜单选择Device Drivers。 Device Drivers下面&#xff0c;找到USB support。 在USB support下面&#xff0c;就可以对USB相关的item进行设置。 按照从上到下的顺序&#xff0c;打开的设置依次…

【vue3】命令式组件封装,message封装示例;(函数式组件?)

仅做代码示例&#xff1b;当然改进的地方还是不少的&#xff0c;仅作为该类组件封装方式的初步启发&#xff1b; 理想大成肯定是想要像 饿了么 这些组件库一样。 有的人叫这函数式组件&#xff0c;有的人叫这命令式组件&#xff0c;我个人还是偏向于命令式组件的称呼。因为以vu…

备战蓝桥杯————差分数组2

目录 引言 一、拼车 题目描述 解题思路及代码 结果展示 二、航班预定统计 题目描述 解题思路及代码 结果展示 总结 引言 在现代交通管理中&#xff0c;拼车服务和航班预订系统是提高资源利用效率、优化用户体验的关键技术。随着城市交通压力的增大和航空业的快速发…

深度学习 精选笔记(4)线性神经网络-交叉熵回归与Softmax 回归

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

为什么网站页面没有被百度搜索收录?是网站被攻击了?

例如&#xff0c;为什么网站页面没有被百度搜索收录&#xff1f; 网站是否受到攻击&#xff1f; 网站索引量和网站流量之间有关系吗&#xff1f; 您在运行网站或小程序时是否有过这样的疑问&#xff1f; 下面我将为大家详细解答这些问题。 1.PC/H5站点相关 1、为什么新网站页面…