用户登录流程以及代码实现

 

 

 

后端

@Override
public Result login(ConsumeUser consumeUser) {
ConsumeUser consumeUser1 = query().eq("account",consumeUser.getAccount()).eq("password",consumeUser.getPassword()).one();
if (consumeUser1 != null) {
String token = UUID.randomUUID().toString();
UserDTO userDTO = BeanUtil.copyProperties(consumeUser1, UserDTO.class);
Map<String, Object> map=BeanUtil.beanToMap(userDTO,new HashMap<>(), CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((filedName,filedValue)->filedValue.toString())
);
redisTemplate.opsForHash().putAll(LOGIN_USER_KEY+token,map);
redisTemplate.expire(LOGIN_USER_KEY+token,LOGIN_USER_TTL, TimeUnit.MINUTES);
return Result.success(token);
}
return Result.error("未找到用户");
}
package com.example.consumeserver.util;

import com.example.consumeserver.dto.UserDTO;

public class UserHolder {
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

public static void saveUser(UserDTO user){
tl.set(user);
}

public static UserDTO getUser(){
return tl.get();
}

public static void removeUser(){
tl.remove();
}
}
登录拦截器
package com.example.consumeserver.util;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;

import io.netty.util.internal.StringUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import java.util.Map;
import java.util.concurrent.TimeUnit;


public class LoginInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(UserHolder.getUser() == null){
response.setStatus(401);
return false;
}
return true;
}
}
刷新token拦截器
package com.example.consumeserver.interceptor;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.example.consumeserver.dto.UserDTO;
import com.example.consumeserver.util.RedisConstants;
import com.example.consumeserver.util.UserHolder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.example.consumeserver.util.RedisConstants.LOGIN_USER_KEY;


public class RefreshTokenInterceptor implements HandlerInterceptor {

private StringRedisTemplate stringRedisTemplate;
public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token=request.getHeader("authorization");
if(StrUtil.isBlank(token)){
return true;
}
String key=LOGIN_USER_KEY + token;
Map<Object, Object> user = stringRedisTemplate.opsForHash().entries(key);
if(user.isEmpty()){
return true;
}
UserDTO userDTO = BeanUtil.fillBeanWithMap(user, new UserDTO(), false);
UserHolder.saveUser(userDTO);
stringRedisTemplate.expire(key, RedisConstants.LOGIN_USER_TTL, TimeUnit.SECONDS);
return true;
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.removeUser();
}
}
配置文件
package com.example.consumeserver.config;


import com.example.consumeserver.util.LoginInterceptor;
import com.example.consumeserver.interceptor.RefreshTokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

@Resource
private StringRedisTemplate stringRedisTemplate;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.excludePathPatterns(
"/login/**"
).order(1);
registry.addInterceptor(new RefreshTokenInterceptor(stringRedisTemplate)).order(0);
}
}
前端实现
<template>
    <div>
            <el-form ref="form" :model="consumeUser" label-width="80px">
                <el-form-item label="账号">
                    <el-input v-model="consumeUser.account"></el-input>
                </el-form-item>
                <el-form-item label="密码">
                    <el-input v-model="consumeUser.password"></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="onSubmit">登录</el-button>
                    <el-button>取消</el-button>
                </el-form-item>
            </el-form>
    </div>
</template>

<script>
import axios from 'axios';
export default {
    data() {
        return {
            consumeUser: {
                account: '',
                password: ''
            },
        }
    },
    methods: {
        onSubmit() {
            axios.post('/api/login', this.consumeUser).then(res => {
                const data = res.data;
                if (data.code == 0) {
                    alert(data.msg)
                    return;
                }
                this.$message.success("登录成功");
                sessionStorage.setItem("token", data.data);
                this.$router.push('/menu')
            }).catch(err =>{
                console.log(err)
            })
        }
    }
}
</script>
登录成功后界面
<template>
    <div>
        <el-container style="height:auto; border: 1px solid #eee">
            <el-aside width="250px" style=" border: 1px solid #d4b6db">
                <el-menu :router="true" :default-active="defaultActive" background-color="#545c64" text-color="#fff"
                    active-text-color="#ffd04b" :active-class="activeClass">
                    <el-submenu index="1">
                        <template slot="title"><i class="el-icon-star-on"></i><span>账单管理</span></template>
                        <el-menu-item index="/bill">账单一览</el-menu-item>
                    </el-submenu>
                    <el-submenu index="2">
                        <template slot="title"><i class="el-icon-platform-eleme"></i><span>个人信息管理</span></template>
                        <el-menu-item index="/user">个人信息</el-menu-item>
                    </el-submenu>
                </el-menu>
            </el-aside>

            <el-container>
                <el-header style="font-size: 40px; background-color: rgb(238, 241, 246)">
                    <div>
                        消费通
                        <div style="float: right;">
                            <img v-if="user.url" :src="user.url" alt="User Avatar" class="user-avatar"
                                style="max-width: 30px; max-height: 30px;" />
                            <span v-if="user.name" style="margin-left: 10px;font-size: 30px">{{ user.name }}</span>
                            <span v-else>Loading...</span>
                        </div>
                    </div>
                </el-header>
                <el-main>
                    <div>
                        <router-view />
                    </div>
                </el-main>
            </el-container>
        </el-container>
    </div>
</template>

<script>
import axios from 'axios';
export default {
    data() {
        return {
            activeClass: 'router-link-active',
            defaultActive: '/bill',
            submenuOpen: '1',
            user: {
                userId: '',
                name: '',
                url: ''
            }
        }
    },
    methods: {
    },
    created() {
        let token = sessionStorage.getItem("token");
        axios.interceptors.request.use(
            config => {
                if (token) {
                    config.headers['authorization'] = token
                } else {
                    this.$message.error('登录失效,请重新登录')
                    this.$router.push('/login') // 使用 Vue Router 跳转
                }
                return config
            },
            error => {
                console.log(error)
                if (error.response && error.response.status === 401) {
                    this.$router.push('/login');
                }
            }
        )
        axios.get('/api/user/me').then(res => {
            this.user = res.data.data;
        }).catch(err => {
            console.log(err);
        })
    },
}
</script>

<style>
.el-header,
.el-footer {
    background-color: #B3C0D1;
    color: #333;
    text-align: center;
    line-height: 60px;
}

.el-aside {
    background-color: #D3DCE6;
    color: #333;
    text-align: center;
    line-height: 200px;
}

.el-main {
    background-color: #E9EEF3;
    color: #333;
    text-align: center;
    line-height: 160px;
}

body>.el-container {
    margin-bottom: 40px;
}

.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
    line-height: 260px;
}

.el-container:nth-child(7) .el-aside {
    line-height: 320px;
}

a {
    text-decoration: none;
    color: #333;
    /* 默认颜色 */
}

/* 鼠标悬停时的样式 */
a:hover {
    color: #ff0000;
    /* 悬停时的颜色 */
}

/* 点击后的样式 */
a:active {
    color: #00ff00;
    /* 点击后的颜色 */
}

.router-link-active {
    text-decoration: none;
    color: #ffd04b;
}
</style>
 
posted @ 2024-04-29 13:30  小趴菜且能喝66  阅读(6)  评论(0)    收藏  举报