SpringBoot+Vue+Redis实现单点登录(一处登录另一处退出登录)

下面是 "SpringBoot+Vue+Redis实现单点登录(一处登录另一处退出登录)" 的完整攻略。

一、前置知识

在讲解实现单点登录的过程中,我们需要掌握以下技术:

  • SpringBoot:后端框架,用来提供 RESTful API 服务;
  • Vue:前端框架,用来构建单页应用;
  • Redis:一个内存数据库,用来保存用户会话信息。

如果对这些技术还不太了解,可以先学习相关的基础知识,再来实践本文的内容。

二、实现思路

单点登录(Single Sign-On)是一种让用户只要登录一次就可以在多个应用中享受无需重复登录的服务。本文实现的单点登录方案,基于 Token 的方式实现,如果用户在一处登录,则可以在其他处直接使用该 Token,而不需要再次输入用户名和密码。当用户在其中一处退出登录时,其他已登录的站点也会失效。

实现该方案的关键点在于 Token 的生成和获取,以及 Token 的存储和验证。下面我们将针对这些点逐一进行讲解。

三、Token 的生成和获取

我们采用 JWT(JSON Web Token)颁发 Token,具体实现过程:

  • 用户在登录成功后,后端服务器使用 JWT 生成 Token,并返回给前端;
  • 前端将 Token 保存在 localStorage 中;
  • 前端携带 Token 向后端请求 API。

当用户在其他页面打开站点时,可以通过读取 localStorage 中保存的 Token,自动向后端请求 API,实现直接登录。

四、Token 的存储和验证

Token 的存储我们使用 Redis,将 Token 以键值对的形式存储在 Redis 中,Key 为 Token 的值,Value 为对应的 user_id。

Token 的验证我们可以利用 SpringBoot 的拦截器,在请求进入后台服务之后,使用 Redis 查询该 Token 对应的 user_id 是否存在,如果存在,则允许请求,如果不存在,则返回未登录状态。

五、示例说明

下面我们来看两个示例,分别说明 Token 的生成和存储,以及 Token 的验证。

1. Token 的生成和存储

后端代码

  1. 引入 JWT 库和 Redis 库。
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 登录生成 Token。
@PostMapping("/login")
public ResponseEntity login(@RequestBody User user) {
    // 校验用户名和密码,生成token
    String token = Jwts.builder()
        .setSubject(user.getUserId())
        .setExpiration(new Date(System.currentTimeMillis() + refreshTokenExpTime))
        .signWith(SignatureAlgorithm.HS512, secret)
        .compact();
    // 存储到redis中
    redisTemplate.opsForValue().set(token, user.getUserId(), refreshTokenExpTime, TimeUnit.MILLISECONDS);

    return ResponseEntity.ok(new Auth(token, refreshTokenExpTime));
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Auth {
    private String token;
    private Long expireTime;
}

前端代码

  1. 登录成功后存储 Token。
export function login(username, password) {
    return request({
        url: '/login',
        method: 'post',
        data: {
            username,
            password
        }
    }).then(response => {
        const {token, expireTime} = response.data;

        localStorage.setItem('token', token);
        localStorage.setItem('expireTime', expireTime);

        return Promise.resolve(response.data);
    });
}

2. Token 的验证

后端代码

  1. 配置 JwtTokenInterceptor 拦截器。
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
    @Autowired
    private JwtTokenInterceptor jwtTokenInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtTokenInterceptor)
                .excludePathPatterns("/login", "/error", "/static/**");
    }
}
  1. 编写 JwtTokenInterceptor 拦截器验证 Token 有效性。
public class JwtTokenInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("Authorization");

        if (token == null) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }

        // 从redis缓存中查找该key
        String userId = redisTemplate.opsForValue().get(token);

        if (userId == null) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return false;
        }

        // 将该user_id和token放入threadLocal中,方便后续使用
        UserInfoHolder.setUserId(userId);
        UserInfoHolder.setToken(token);

        return true;
    }
}

前端代码

对于前端来说,我们只需要在请求头中加上 Token 即可。

import axios from 'axios';
import router from '../router';

const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_API,
    timeout: 5000
});

service.interceptors.request.use(
    config => {
        if (localStorage.getItem('token')) {
            // 如果 Token 存在,则在请求头中加入
            config.headers.Authorization = localStorage.getItem('token');
        }

        return config;
    },
    error => {
        return Promise.reject(error);
    }
);

service.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        // 如果请求返回的状态码为 401,表示未经授权,则跳转到登录页
        if (error.response.status === 401) {
            router.push('/login');
        }

        return Promise.reject(error)
    }
);

export default service;

以上就是实现单点登录方案的完整攻略,通过 Token 的存储和验证,我们可以在多个应用中实现单点登录的功能。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot+Vue+Redis实现单点登录(一处登录另一处退出登录) - Python技术站

(0)
上一篇 2023年6月9日
下一篇 2023年6月9日

相关文章

  • win7系统设置网页背景颜色如绿色和豆绿色来保护眼睛

    请你先了解一下markdown的基本语法,以便于理解本文本中的标记和格式。首先,我们需要了解如何更改网页的背景颜色。在HTML中,可以通过设置CSS样式来实现此功能。因此,我们需要在网页head标签内添加一个style标签,并在其中设置相应的颜色值。以下是一个例子。 步骤一:打开Win7系统的控制面板 点击Win7系统的“开始”按钮,在“开始”菜单中选择“控…

    css 2023年6月9日
    00
  • JS+CSS实现仿支付宝菜单选中效果代码

    下面我将为你详细讲解“JS+CSS实现仿支付宝菜单选中效果代码”的完整攻略。 背景 支付宝的菜单选中效果非常优美,用户对于选中菜单项有一个非常直观的反馈。因此,很多网站尝试模仿这种效果,提高用户的体验。 效果演示 在开始之前,我先给你演示一下最终的效果。你可以访问以下链接,预览动态效果: JS+CSS实现仿支付宝菜单选中效果 实现步骤 下面是实现仿支付宝菜单…

    css 2023年6月10日
    00
  • 微信小程序在text文本实现多种字体样式

    下面是详细讲解关于“微信小程序在text文本实现多种字体样式”的完整攻略。 1. 利用rich-text标签 在微信小程序中,需要实现多种字体样式时,可以使用 rich-text 标签。该标签可以渲染包含 HTML 标签的文本,并支持使用 style 属性指定 CSS 样式。下面是 rich-text 的一个示例: <rich-text nodes=&…

    css 2023年6月10日
    00
  • Html5剪切板功能的实现代码

    针对 Html5 剪切板功能的实现代码,以下是完整攻略: 1. HTML5 剪切板简介 HTML5 引入了剪切板 API,它允许你使用 JavaScript 来访问用户剪贴板中的数据,并且可以通过 API 将数据写入到剪贴板。 2. 代码示例 2.1 复制文本到剪贴板 function copyToClipboard(text) { const temp =…

    css 2023年6月9日
    00
  • 十个不为人知的Photoshop文本排版工具详解

    十个不为人知的Photoshop文本排版工具详解 1. 风格预设 在Photoshop中,可以通过风格预设(Style Presets)轻松快捷地为文本添加独特的样式。选择一个文本层,点击“图层样式”(Layer Style)按钮,在弹出的面板中选择“风格预设”(Style Presets),从中选择一个你喜欢的样式即可。 2. 字母间距/字符间距 字母(字…

    css 2023年6月9日
    00
  • CSS first-chjld伪类属性匹配一个序列的第一个元素

    CSS中的:first-child伪类选择器可以匹配一个元素的第一个子元素。这意味着,如果一个元素有多个子元素,:first-child伪类选择器会选择第一个子元素。相应地,CSS中的:first-of-type伪类选择器也可以选择一个元素的第一个指定类型的子元素。但是有时候我们想要选择一个序列的第一个元素,而不仅仅是该元素的子元素。为了实现这个目的,我们可…

    css 2023年6月10日
    00
  • PHP实现动态删除XML数据的方法示例

    下面是详细讲解“PHP实现动态删除XML数据的方法示例”的完整攻略。 标题一:背景和前置知识 在开发PHP应用程序时,我们常常需要对XML数据进行增删改查操作。其中删除操作是非常常见的,可以用来删除某个具体节点、某一类节点等。本文将介绍通过PHP实现动态删除XML数据的方法。 在阅读本文之前,需要你已经掌握PHP基础语法和XML基础知识,并安装了PHP解释器…

    css 2023年6月10日
    00
  • css实现文字垂直居中的代码第1/2页

    当需要将文字垂直对齐到容器中心时,可以使用CSS的flexbox布局或者行高+伪元素的方式实现。下面分别对这两种方法进行说明。 方法一:flexbox布局 Flexbox布局允许我们通过align-items属性将元素的内容垂直居中。具体实现方式如下: 创建一个容器元素,并设置display为flex以启用flexbox布局。 通过align-items属性…

    css 2023年6月9日
    00
合作推广
合作推广
分享本页
返回顶部