配置MySQL与登录模块

news2024/9/23 8:39:23

 使用技术

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.coloradmin.cn/o/1483687.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink状态存储-StateBackend

文章目录 前言一、MemoryStateBackend二、FSStateBackend三、RocksDBStateBackend四、StateBackend配置方式五、状态持久化六、状态重分布OperatorState 重分布KeyedState 重分布 七、状态过期 前言 Flink是一个流处理框架&#xff0c;它需要对数据流进行状态管理以支持复杂的…

猫头虎分享已解决Bug || AttributeError: ‘str‘ object has no attribute ‘decode‘

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

pycharm专业版本的安装

一 、到官网下载对应的pycharm安装包 也可以把安装软件&#xff08;用物理机下载到共享文件夹&#xff09; 然后进入Ubuntu系统把下载大的安装包剪贴到目标路径 1 在ubuntu中创建一个用来存放pycharm安装包的文件夹 rootzmq-virtual-machine:/home/zmq/Desktop# mkdir pycha…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的教室人员检测与计数(Python+PySide6界面+训练代码)

摘要&#xff1a;开发教室人员检测与计数系统对于优化教学资源和提升教学效率具有重要意义。本篇博客详细介绍了如何利用深度学习构建此系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5的性能&#xff0c;展示…

初识Maven

介绍&#xff1a; web后端开发技术ApacheMaven是一个项目管理和构建工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff09;的概念&#xff0c;通过一小段描述信息来管理项目的构建。安装&#xff1a;http://maven.apache.org/ Apache软件基金会&#xff0c;成立于19…

Leetcode42. 接雨水 -hot100

题目&#xff1a; 代码(首刷自解 2024年3月2日 有3个案例超时&#xff09;&#xff1a; 不算完全做出来&#xff0c;看了答案了&#xff0c;等以后二刷吧 class Solution { public:int helper(const vector<int>& height,const int high) {for(int i 0; i < hei…

对单元测试的思考(稳定性建设)

单测是很常见的技术的名词&#xff0c;但背后的逻辑和原理你是否清楚&#xff0c;让我们一起review一下。 1. 单测是什么&#xff1f;&#x1f914; 单测是单元测试,主要是测试一个最小逻辑块。比如一个函数、一个react、vue 组件。 2.为什么要写单测&#xff1f;&#x1f9…

扩展学习|大数据分析的现状和分类

文献来源&#xff1a;[1] Mohamed A , Najafabadi M K , Wah Y B ,et al.The state of the art and taxonomy of big data analytics: view from new big data framework[J].Artificial Intelligence Review: An International Science and Engineering Journal, 2020(2):53. 下…

windows系统下安装RabbitMQ

一、RabbitMQ安装软件资源准备 因为RabbitMQ是Erlang语言开发的&#xff0c;因此安装Erlang环境在进行安装RbbitMQ的操作&#xff0c;选择两者版本时一定要参考版本的兼容性 1.RabbitMQ国内下载地址&#xff0c;因官网下载比较缓慢&#xff0c;还是国内的稍微快些 https://r…

CTFHUB--文件包含漏洞--RCE

文件包含漏洞 文件包含漏洞也是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行。有时候由于网站功能需求&#xff0c;会让前端用户选择要包含的文件&#xff0c;而开发人员又没有对要包含的文件进行安全考虑&#xff0c;…

Linux yum安装pgsql出现Bad GPG signature错误

官方文档&#xff1a;https://www.postgresql.org/download/linux/redhat/ sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo yum install -y postgresql12-server sudo /usr/pgsql-12/bin/…

Angular 由一个bug说起之四:jsonEditor使用不当造成的bug

一&#xff1a;问题 项目中使用了一个JSON第三方库&#xff1a; GitHub - josdejong/jsoneditor: A web-based tool to view, edit, format, and validate JSON 当用户编辑JSON格式的数据&#xff0c;查找替换时&#xff1a; 用户的期望结果是&#xff1a;$$ 被替换为$$_text&a…

使用 Azure 部署静态网页

Author&#xff1a;AXYZdong 硕士在读 工科男 有一点思考&#xff0c;有一点想法&#xff0c;有一点理性&#xff01; 定个小小目标&#xff0c;努力成为习惯&#xff01;在最美的年华遇见更好的自己&#xff01; CSDNAXYZdong&#xff0c;CSDN首发&#xff0c;AXYZdong原创 唯…

ROS开发基础-Linux基础第四部(开发板设置本地IP)

一 、网线连接设备 使用网线连接jetson NX与机械臂&#xff0c;如下图所示&#xff1a; 二、 修改上位机IPV4 IP ①测试是否可连接。网线连接机械臂之后&#xff0c;在桌面打开终端输入命令“ping 192.168.1.18”,如不可正常通信&#xff0c;可按照下述步骤进行设置。 ②在U…

React富文本编辑器开发(一)

这是一个系统的完整的教程&#xff0c;每一节文章的内容都很重要。这个教程学完后自己可以开发出一个相当完美的富文本编辑器了。下面就开始我们今天的内容&#xff1a; 安装 是的&#xff0c;我们的开发是基于Slate的开发基础&#xff0c;所以要安装它&#xff1a; yarn ad…

Go 互斥锁的实现原理?

Go sync包提供了两种锁类型&#xff1a;互斥锁sync.Mutex 和 读写互斥锁sync.RWMutex&#xff0c;都属于悲观锁。 概念 Mutex是互斥锁&#xff0c;当一个 goroutine 获得了锁后&#xff0c;其他 goroutine 不能获取锁&#xff08;只能存在一个写者或读者&#xff0c;不能同时…

Neural Network Diffusion论文解读

详细解读&#xff1a;扩散模型生成神经网络参数&#xff0c;速度快了44倍&#xff1a;Neural Network Diffusion论文解读 摘要&#xff1a; 扩散模型在图像和视频生成方面取得了显著的成功。在这项工作中&#xff0c;我们证明了扩散模型也可以生成高性能的神经网络参数。我们…

程序员的金三银四求职宝典!

目录 ​编辑 程序员的金三银四求职宝典 一、为什么金三银四是程序员求职的黄金时期&#xff1f; 二、如何准备金三银四求职&#xff1f; 1. 完善简历 2. 增强技术能力 3. 提前考虑目标公司 4. 提前准备面试 三、程序员求职的常见面试题 1. 数据结构和算法 2. 数据库 …

在原有pytorch环境下安装DGL库和其对应的CUDA【自用】

前段时间看到一篇AAAI2024的论文Patch-wise Graph Contrastive Learning for Image Translation&#xff0c;它采用GNN的思想来进行image-to-image translation的任务&#xff0c;非常的新颖&#xff0c;但我进行复现的时候&#xff0c;发现直接下载它里面需要的DGL库是无法运行…

旧的Spring Security OAuth已停止维护,全面拥抱新解决方案Spring SAS

Spring Authorization Server 替换 Shiro 指引 背景 Spring 团队正式宣布 Spring Security OAuth 停止维护&#xff0c;该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用作为 SpringBoot 3.0 的最新…