目录
当我们未登录的时候所有的页面都会重定向到我们的登录页面
强行让用户登录一下,我们可以在router里面实现
API意思,to跳转到哪个页面,from表示从哪个页面跳转过去
next的表示将页面要不要执行下一步操作,写之前首先要记录每一个未授权界面
把一些额外信息放到一个额外的域里面,meta信息里面存一下是否要授权
如果需要授权而且没有登录,重定向到登录页面
在 router 目录下的 index.js 文件下实现。 router -> index.js
- import { createRouter, createWebHistory } from 'vue-router';
- import PkIndexView from '@/views/pk/PkIndexView';
- import RankListIndexView from '@/views/ranklist/RankListIndexView';
- import RecordIndexView from '@/views/record/RecordIndexView';
- import UserBotsIndexView from '@/views/user/userbots/UserBotsIndexView';
- import UserInfoIndexView from '@/views/user/userinfo/UserInfoIndexView';
- import MiNiChatView from '@/views/chat/MiNiChatView';
- import NotFound from '@/views/error/NotFound';
- import HomeIndexView from '@/views/home/HomeIndexView';
- import UserAccountLoginView from '@/views/user/UserAccountLoginView';
- import UserAccountRegisterView from '@/views/user/UserAccountRegisterView';
- import { from } from 'core-js/core/array';
- import store from '@/store';
-
- // 把一些额外信息放到一个额外的域里面,meta信息里面存一下是否要授权,如果需要授权而且没有登录,重定向到登录页面,重定向到登录界面。
- const routes = [
-
- {
- path: "/",
- name: "home",
- component: HomeIndexView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/pk/",
- name: "pk_index",
- component: PkIndexView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/record/",
- name: "record_index",
- component: RecordIndexView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/ranklist/",
- name: "ranklist_index",
- component: RankListIndexView,
- },
- {
- path: "/user/bot/",
- name: "user_bot_index",
- component: UserBotsIndexView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/user/info/",
- name: "user_info_index",
- component: UserInfoIndexView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/user/login/",
- name: "user_account_login",
- component: UserAccountLoginView,
- meta:{
- requestAuth: false,
- }
- },
- {
- path: "/user/register/",
- name: "user_account_register",
- component: UserAccountRegisterView,
- meta:{
- requestAuth: false,
- }
- },
- {
- path: "/user/chat/",
- name: "user_chat_index",
- component: MiNiChatView,
- meta:{
- requestAuth: true,
- }
- },
- {
- path: "/404/",
- name: "404",
- component: NotFound,
- meta:{
- requestAuth: false,
- }
- },
- {
- path: "/:catchAll(.*)",
- redirect: "/404/",
- }
-
- ]
- const router = createRouter({
- history: createWebHistory(),
- routes
- })
- // to跳转到哪个页面, from表示从哪个页面跳转过去
- // next的表示将页面要不要执行下一步操作,写之前首先要记录每一个未授权界面
- router.beforeEach((to,from,next) =>{
- //如果要跳转的页面需要登录,但是没有登录,就强制到登录界面
- if(to.meta.requestAuth && !store.state.user.is_login){
- next({name:"user_account_login"});
- }else{
- next();
- }
- });
-
- export default router
最终实现效果:如果处于未登录状态,点击 除注册之外的按钮 页面会跳转到 登录界面。
访问公开的链接不需要加表头,其他的都需要,实现注册页面直接把登录页面的样式复制过来再做修改
如果是export的default就可以不加括号任意取名
如果export的时候不加default就必须加上大括号
提交的时候需要触发register函数,需要自己定义一下
用let const都可以,当变量不会重新赋值用const可能会重新赋值用let
我们可以把登录的api抄过来,如果是获取数据不用修改数据你就用get
一般如果会修改数据库的话就用post,安全性post比get强
在 view -> user -> account 下的 UserAccountRegisterView.vue 文件实现,实现方式类似于同目录下的 UserAccountLoginView.vue
可以直接把登录页面的样式复制过来再做修改。
-
-
-
-
-
-
WELCOME
-
JOIN US!
-
-

-
-
-
-
-
-
-
Login
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
No account? To register
-
-
-
-
-
-
-
Register
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Login with an existing account
-
-
-
-
-
-
-
- import { ref } from 'vue';
- import { useRouter } from 'vue-router';
- import { useStore } from 'vuex';
- import $ from 'jquery';
- export default{
- setup(){
-
- const router = useRouter();
- const store = useStore();
- let login_username = ref('');
- let login_password = ref('');
- let register_username = ref('');
- let register_password = ref('');
- let confirmedPassword = ref('');
- let error_message = ref('');
-
- //触发函数
- const login = ()=>{
- error_message.value = "";
- // 调用store 里actions的函数
- // login 对应函数名 后面的对应 data
- store.dispatch("login",{
- //用 ref 获取值要用value
- username:login_username.value,
- password:login_password.value,
- success(){
- //成功后获取用户信息
- store.dispatch("getinfo",{
- success(){
- router.push({name:'home'});
- console.log(store.state.user);
- }
- })
- },
- error(){
- error_message.value = "用户名或密码错误";
- }
- })
- }
-
- const register = () => {
- $.ajax({
- url: "http://127.0.0.1:3000/user/account/register/",
- type: "post",
- data: {
- username: register_username.value,
- password: register_password.value,
- confirmedPassword: confirmedPassword.value,
- },
- success(resp) {
- // 成功直接返回登录界面
- if (resp.error_message === "success") {
- router.push({name:"user_account_login"})
- } else {
- error_message.value = resp.error_message;
- }
- },
- });
-
- }
- let flag = ref(false);
- const mySwitch = () => {
- const pre_box = document.querySelector('.pre-box')
- const img = document.querySelector("#avatar")
- if (flag.value) {
- pre_box.style.transform = "translateX(0%)"
- pre_box.style.backgroundColor = "#edd4dc"
- img.src = require("@/assets/images/menu_op.jpg")
- }
- else {
- pre_box.style.transform = "translateX(100%)"
- pre_box.style.backgroundColor = "#c9e0ed"
- img.src = require("@/assets/images/menu_op2.jpg")
- }
- flag.value = !flag.value
- }
-
- return{
- login_username,
- login_password,
- register_username,
- register_password,
- confirmedPassword,
- error_message,
- register,
- router,
- login,
- mySwitch,
- }
- }
- }
-
-
-
-
- /* 去除input的轮廓 */
- input {
- outline: none;
- }
- .bigBox {
- /* 溢出隐藏 */
- height: 100vh;
- overflow-x: hidden;
- display: flex;
- /* 渐变方向从左到右 */
- background: linear-gradient(to right, rgb(247, 209, 215), rgb(191, 227, 241));
- }
- /* 最外层的大盒子 */
- .box {
- width: 1050px;
- height: 600px;
- display: flex;
- /* 相对定位 */
- position: relative;
- z-index: 2;
- margin: auto;
- /* 设置圆角 */
- border-radius: 8px;
- /* 设置边框 */
- border: 1px solid rgba(255, 255, 255, 0.6);
- /* 设置盒子阴影 */
- box-shadow: 2px 1px 19px rgba(0, 0, 0, 0.1);
- }
- /* 滑动的盒子 */
- .pre-box {
- /* 宽度为大盒子的一半 */
- width: 50%;
- height: 100%;
- /* 绝对定位 */
- position: absolute;
- /* 距离大盒子左侧为0 */
- left: 0;
- /* 距离大盒子顶部为0 */
- top: 0;
- z-index: 99;
- border-radius: 4px;
- background-color: #edd4dc;
- box-shadow: 2px 1px 19px rgba(0, 0, 0, 0.1);
- /* 动画过渡,先加速再减速 */
- transition: 0.5s ease-in-out;
- }
- /* 滑动盒子的标题 */
- .pre-box h1 {
- margin-top: 150px;
- text-align: center;
- /* 文字间距 */
- letter-spacing: 5px;
- color: white;
- /* 禁止选中 */
- user-select: none;
- /* 文字阴影 */
- text-shadow: 4px 4px 3px rgba(0, 0, 0, 0.1);
- }
- /* 滑动盒子的文字 */
- .pre-box p {
- height: 30px;
- line-height: 30px;
- text-align: center;
- margin: 20px 0;
- /* 禁止选中 */
- user-select: none;
- font-weight: bold;
- color: white;
- text-shadow: 4px 4px 3px rgba(0, 0, 0, 0.1);
- }
- /* 图片盒子 */
- .img-box {
- width: 200px;
- height: 200px;
- margin: 20px auto;
- /* 设置为圆形 */
- border-radius: 50%;
- /* 设置用户禁止选中 */
- user-select: none;
- overflow: hidden;
- box-shadow: 4px 4px 3px rgba(0, 0, 0, 0.1);
- }
- /* 图片 */
- .img-box img {
- width: 100%;
- transition: 0.5s;
- }
- /* 登录和注册盒子 */
- .login-form,
- .register-form {
- flex: 1;
- height: 100%;
- }
- /* 标题盒子 */
- .title-box {
- height: 150px;
- line-height: 500px;
- }
-
- /* 标题 */
- .title-box h1 {
- padding-top: 50px;
- text-align: center;
- color: white;
- /* 禁止选中 */
- user-select: none;
- letter-spacing: 5px;
- text-shadow: 4px 4px 3px rgba(0, 0, 0, 0.1);
- }
- /* 输入框盒子 */
- .el-form {
- display: flex;
- /* 纵向布局 */
- flex-direction: column;
- /* 水平居中 */
- align-items: center;
- }
- .el-form-item {
- width: 65%;
- }
- /* 输入框 */
- input {
- /* width: 60%; */
- height: 40px;
- margin-bottom: 20px;
- text-indent: 10px;
- border: 1px solid #fff;
- background-color: rgba(255, 255, 255, 0.3);
- border-radius: 120px;
- /* 增加磨砂质感 */
- backdrop-filter: blur(10px);
- }
- input:focus {
- /* 光标颜色 */
- color: #b0cfe9;
- }
- /* 聚焦时隐藏文字 */
- input:focus::placeholder {
- opacity: 0;
- }
- /* 按钮盒子 */
- .btn-box {
- display: flex;
- justify-content: center;
- }
- /* 按钮 */
- button {
- width: 100px;
- height: 30px;
- margin: 0 7px;
- line-height: 30px;
- border: none;
- border-radius: 4px;
- background-color: #69b3f0;
- color: white;
- }
- /* 按钮悬停时 */
- button:hover {
- /* 鼠标小手 */
- cursor: pointer;
- /* 透明度 */
- opacity: 0.8;
- }
- /* 按钮文字 */
- .btn-box p {
- height: 30px;
- line-height: 30px;
- /* 禁止选中 */
- user-select: none;
- font-size: 14px;
- color: white;
- }
- .btn-box p:hover {
- cursor: pointer;
- border-bottom: 1px solid white;
- }
- .error-message{
- color: red;
- }
-
在测试的时候可能会遇到不输入密码也可以注册成功的 bug, 在 RegisterServiceImpl.java 下 修改一下就可以了。
当我们的用户重定向到登陆页面的时候,我们可以事先判断一下他们有没有将token存在本地
如果存在本地就把token取出来,验证一下是否过期
如果没有过期,用户就不需要重新登陆了,可以直接跳转到首页
刷新之后其实是一个未登录状态,自动先跳到我们的登录页面
先从本地把我们的token取出来,发现token存在把token
更新到我们的内存当中,然后再从云端请求下这个用户的信息
如果可以请求的信息表示我们的token,是有效的,
表示用户是登录的,我们就可以跳转到首页
当我们的用户重定向到登陆页面的时候,我们需要把用户的 token 存储到浏览器的 local storage,这样就可以实现登录状态持久化。
首先 修改 store 目录下的 -> user.js 文件,在合适的位置添加下列两行。
- localStorage.setItem("jwt_token", resp.token);
-
- localStorage.removeItem("jwt_token");
其次 修改 view -> user -> account 下的 UserAccountLoginView.vue 文件
- 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(){
- store.commit("updatePullingInfo",false);
- }
- })
- }else{
- store.commit("updatePullingInfo",false);
- }
优化前端
在实现前端登录状态持久化之后,刷新页面可能会存在明显的转换,所以下面对前端页面进行优化。
首先 在 store 目录下的 user.js 中添加全局变量和下拉函数。
- import $ from 'jquery'
-
- export default{
- state:{
- id:"",
- username:"",
- password:"",
- photo:"",
- token:"",
- is_login:false,
- pulling_info:true,//是否正在拉取信息
- },
- getters:{
-
- },
- mutations:{//修改state里面的值
- 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){
- // 测试LoginService
- $.ajax({
- url:"http://127.0.0.1:3000/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);
- //调用 mutations里面的函数用commit context上下文调用mutations里面的函数 修改state的内容
- context.commit("updateToken",resp.token);
- //调用data的成功函数
- data.success(resp);
- }
- },
- error(resp){
- data.error(resp);
- }
- });
- },
- getinfo(context,data){
- $.ajax({
- url:"http://127.0.0.1:3000/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:{
-
- },
- }
其次 修改 UserAccountLoginView.vue
最后还需要修改 NavBar.vue。
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" >
- Login
- router-link>
- li>
- <li class="nav-item ">
- <router-link class="nav-link " :to="{name:'user_account_register'}" role="button" >
- Register
- router-link>
- li>
-
完成~