SpringBoot+JWT实现单点登录解决方案

这篇具有很好参考价值的文章主要介绍了SpringBoot+JWT实现单点登录解决方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、什么是单点登录?

单点登录是一种统一认证和授权机制,指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的系统,不需要重新登录验证。

单点登录一般用于互相授信的系统,实现单一位置登录,其他信任的应用直接免登录的方式,在多个应用系统中,只需要登录一次,就可以访问其他互相信任的应用系统。

随着时代的演进,大型web系统早已从单体应用架构发展为如今的多系统分布式应用群。但无论系统内部多么复杂,对用户而言,都是一个统一的整体,访问web系统的整个应用群要和访问单个系统一样,登录/注销只要一次就够了,不可能让一个用户在每个业务系统上都进行一次登录验证操作,这时就需要独立出一个单独的认证系统,它就是单点登录系统。

二、单点登录的优点

1.方便用户使用。用户不需要多次登录系统,不需要记住多个密码,方便用户操作。

2.提高开发效率。单点登录为开发人员提供类一个通用的验证框架。

3.简化管理。如果在应用程序中加入了单点登录的协议,管理用户账户的负担就会减轻。

三、JWT 机制

JWT(JSON Web Token)它将用户信息加密到token里,服务器不保存任何用户信息。服务器通过使用保存的密钥验证JWTToken的正确性,只要正确就通过验证。

数据结构:

JWT包含三个部分:Header头部,Payload负载和Signature签名。三个部门用“.”分割。校验也是JWT内部自己实现的 ,并且可以将你存储时候的信息从token中取出来无须查库。

JWT执行流程:

JWT的请求流程也特别简单,首先使用账号登录获取Token,然后后面的各种请求,都带上这个Token即可。具体流程如下:

1. 客户端发起登录请求,传入账号密码;

2. 服务端使用私钥创建一个Token;

3. 服务器返回Token给客户端;

4. 客户端向服务端发送请求,在请求头中携带Token;

5. 服务器验证该Token;

6. 返回结果。

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

四.创建Maven父项目

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 2.指定打包类型为pom

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 五.创建认证模块sso

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 1.添加依赖,完整的pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.13</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>sso</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sso</name>
    <description>sso</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.2</version>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2添加jwt相关配置

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 3.创建JWT配置类和JWT工具类

示例代码如下:

package com.example.sso.bean;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author qx
 * @date 2023/7/4
 * @des Jwt配置类
 */
@Component
@ConfigurationProperties(prefix = "jwt")
@Getter
@Setter
public class JwtProperties {

    /**
     * 过期时间-分钟
     */
    private Integer expireTime;

    /**
     * refreshToken时间
     */
    private Integer refreshTime;

    /**
     * 密钥
     */
    private String secret;
}
package com.example.sso.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.sso.bean.JwtProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author qx
 * @date 2023/7/4
 * @des JWT工具类
 */
@Component
public class JwtUtil {

    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 生成一个jwt字符串
     *
     * @param username 用户名
     * @return jwt字符串
     */
    public String sign(String username) {
        Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());
        return JWT.create()
                // 设置过期时间1个小时
                .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getExpireTime() * 60 * 1000))
                // 设置负载
                .withClaim("username", username).sign(algorithm);
    }

    /**
     * 生成refreshToken
     *
     * @param username 用户名
     * @return
     */
    public String refreshToken(String username) {
        Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());
        return JWT.create()
                // 设置更新时间2个小时
                .withExpiresAt(new Date(System.currentTimeMillis() + jwtProperties.getRefreshTime() * 60 * 1000))
                // 设置负载
                .withClaim("username", username).sign(algorithm);
    }


    public static void main(String[] args) {
        Algorithm algorithm = Algorithm.HMAC256("KU5TjMO6zmh03bU3");
        String username = "admin";
        String token = JWT.create()
                // 设置过期时间1个小时
                .withExpiresAt(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
                // 设置负载
                .withClaim("username", username).sign(algorithm);
        System.out.println(token);
    }

    /**
     * 校验token是否正确
     *
     * @param token token值
     */
    public boolean verify(String token) {
        if (token == null || token.length() == 0) {
            throw new RuntimeException("token为空");
        }
        try {
            Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());
            JWTVerifier jwtVerifier = JWT.require(algorithm).build();
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            Map<String, Claim> map = decodedJWT.getClaims();
            System.out.println("claims:" + map.get("username").asString());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }

    /**
     * 重新生成token和refreshToken
     *
     * @param refreshToken refreshToken
     * @return 返回token和refreshToken
     */
    public Map<String, String> refreshJwt(String refreshToken) {
        if (refreshToken == null || refreshToken.length() == 0) {
            throw new RuntimeException("refreshToken为空");
        }
        Algorithm algorithm = Algorithm.HMAC256(jwtProperties.getSecret());
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        DecodedJWT decodedJWT = jwtVerifier.verify(refreshToken);
        Map<String, Claim> map = decodedJWT.getClaims();
        // 获取用户名
        String username = map.get("username").asString();
        Map<String, String> resultMap = new HashMap<>();
        // 重新生成token和refreshToken
        resultMap.put("token", sign(username));
        resultMap.put("refreshToken", refreshToken(username));
        return resultMap;
    }
}

4.创建服务层

package com.example.sso.service;

import com.example.sso.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * @author qx
 * @date 2023/7/4
 * @des 登录服务层
 */
@Service
public class LoginService {


    @Autowired
    private JwtUtil jwtUtil;

    /**
     * 登录
     *
     * @param username 用户名
     * @param password 密码
     * @return token值
     */
    public Map<String, String> login(String username, String password) {
        Map<String, String> map = new HashMap<>();
        if ("".equals(username) || "".equals(password)) {
            throw new RuntimeException("用户名或密码不能为空");
        }
        // 为了测试方便 不去数据库比较密码
        if ("123".equals(password)) {
            // 返回生成的token
            map.put("token", jwtUtil.sign(username));
            map.put("refreshToken", jwtUtil.refreshToken(username));
        }
        return map;
    }

    /**
     * 校验jwt是否成功
     *
     * @param token token值
     * @return 校验是否超过
     */
    public boolean checkJwt(String token) {
        return jwtUtil.verify(token);
    }

    /**
     * 重新生成token和refreshToken
     *
     * @param refreshToken refreshToken
     * @return token和refreshToken
     */
    public Map<String, String> refreshJwt(String refreshToken) {
        return jwtUtil.refreshJwt(refreshToken);
    }
}

5.创建控制层

package com.example.sso.controller;

import com.example.sso.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Map;

/**
 * @author qx
 * @date 2023/7/4
 * @des 验证控制层
 */
@Controller
@RequestMapping("/sso")
public class AuthController {

    @Autowired
    private LoginService loginService;

    /**
     * 登录页面
     */
    @GetMapping("/login")
    public String toLogin() {
        return "login";
    }

    /**
     * 登录
     *
     * @param username 用户名
     * @param password 密码
     * @return token值
     */
    @PostMapping("/login")
    @ResponseBody
    public Map<String, String> login(String username, String password) {
        return loginService.login(username, password);
    }

    /**
     * 验证jwt
     *
     * @param token token
     * @return 验证jwt是否合法
     */
    @RequestMapping("/checkJwt")
    @ResponseBody
    public boolean checkJwt(String token) {
        return loginService.checkJwt(token);
    }


    /**
     * 重新生成token和refreshToken
     *
     * @param refreshToken refreshToken
     * @return token和refreshToken
     */
    @RequestMapping("/refreshJwt")
    @ResponseBody
    public Map<String, String> refreshJwt(String refreshToken) {
        return loginService.refreshJwt(refreshToken);
    }

}

6.创建一个登录页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form method="post" action="/sso/login">
        用户名:<input type="text" name="username"/><br/>
        密码:<input type="password" name="password"/><br/>
        <button type="submit">登录</button>
    </form>
</body>
</html>

六、创建应用系统projectA 

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 1.项目pom文件如下所示

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>my-sso</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>projectA</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.13</version>
        </dependency>
        <!--okhttp-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.10.0</version>
        </dependency>
    </dependencies>
</project>

 2.修改配置文件

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 3.创建过滤器

package com.example.projectA.filter;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author qx
 * @date 2023/7/4
 * @des 登录过滤器
 */
@Component
@WebFilter(urlPatterns = "/**")
public class LoginFilter implements Filter {

    @Value("${sso_server}")
    private String serverHost;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String token = httpServletRequest.getParameter("token");
        if (this.check(token)) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // token过期后再使用refreshToken处理
            String refreshToken = httpServletRequest.getHeader("refreshToken");
            if (check(refreshToken)) {
                // 重新生成token和refreshtoken给客户端保存 下次传递token参数的时候使用这个重新生成的
                System.out.println("更新后的token和refreshToken:" + refreshToken(refreshToken));
                filterChain.doFilter(servletRequest, servletResponse);
            }
            // 如果refreshToken也过期 那么需要重新登录
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            String redirect = serverHost + "/login";
            response.sendRedirect(redirect);
        }
    }

    /**
     * 验证token
     *
     * @param token
     * @return
     * @throws IOException
     */
    private boolean check(String token) throws IOException {
        if (token == null || token.trim().length() == 0) {
            return false;
        }
        OkHttpClient client = new OkHttpClient();
        // 请求验证token的合法性
        String url = serverHost + "/checkJwt?token=" + token;
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        return Boolean.parseBoolean(response.body().string());
    }

    /**
     * 重新获取token和refreshToken
     *
     * @param refreshToken
     * @return
     * @throws IOException
     */
    private String refreshToken(String refreshToken) throws IOException {
        if (refreshToken == null || refreshToken.trim().length() == 0) {
            return null;
        }
        OkHttpClient client = new OkHttpClient();
        // 请求重新获取token和refreshToken
        String url = serverHost + "/refreshJwt?refreshToken=" + refreshToken;
        Request request = new Request.Builder().url(url).build();
        Response response = client.newCall(request).execute();
        return response.body().string();

    }
}

4.创建测试控制层

package com.example.projectA.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author qx
 * @date 2023/7/4
 * @des 测试A
 */
@RestController
public class IndexController {

    @GetMapping("/testA")
    public String testA() {
        return "输出testA";
    }
}

5.启动类

package com.example.projectA;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

/**
 * @author qx
 * @date 2023/7/4
 * @des Projecta启动类
 */
@SpringBootApplication
@ServletComponentScan
public class ProjectaApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProjectaApplication.class,args);
    }
}

6.启动项目测试

我们访问http://localhost:8081/testA 系统跳转到了验证模块的登录页面

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

我们输入账号密码登录成功后返回token

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 如何我们复制这段数据,把数据传递到token参数。

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 我们看到正确获取到了数据。

当我们的token过期之后,我们使用refreshToken重新获取到新的token和refreshToken。

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 由于我们的项目中refreshToken的更新时间为2分钟,我们等这个时间过期之后我们再次请求接口,那么这个时候就要去重新登录了。

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

一般的我们的refreshToken时间设置要比token的过期时间要长。

客户端登录后,将 token和refreshToken 保存在客户端本地,每次访问将 token 传给服务端。服务端校验 accessToken 的有效性,如果过期的话,就将 refreshToken 传给服务端。如果 refreshToken 有效,服务端就生成新的 accessToken 给客户端。否则,客户端就重新登录即可。

七、创建应用系统projectB

我们再次模仿projectA创建projectB子模块。

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

 启动模块B

我们直接测试带上token参数SpringBoot+JWT实现单点登录解决方案,SpringBoot,spring boot,后端,java

通过之前的token,无需登录即可成功进入了应用系统B。说明我们的单点登录系统搭建成功。文章来源地址https://www.toymoban.com/news/detail-523189.html

到了这里,关于SpringBoot+JWT实现单点登录解决方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • token和JWT token区别、登录安全、页面权限、数据权限、单点登录

     直接区别: token需要查库验证token 是否有效,而JWT不用查库或者少查库,直接在服务端进行校验,并且不用查库。因为用户的信息及加密信息在第二部分payload和第三部分签证中已经生成,只要在服务端进行校验就行,并且校验也是JWT自己实现的。     JWT是json web token缩写。

    2023年04月09日
    浏览(37)
  • Java开发 - 单点登录初体验(Spring Security + JWT)

    目录 ​​​​​​​ 前言 为什么要登录 登录的种类 Cookie-Session Cookie-Session-local storage JWT令牌 几种登陆总结  用户身份认证与授权 创建工程 添加依赖 启动项目 Bcrypt算法的工具 创建VO模型类 创建接口文件 创建XML文件 补充配置 添加依赖 添加配置 创建配置类 测试上面的配置

    2024年02月02日
    浏览(36)
  • appium2.0+ 单点触控和多点触控新的解决方案

    在 appium2.0 之前,在移动端设备上的触屏操作,单手指触屏和多手指触屏分别是由 TouchAction 类,Multiaction 类实现的。 在 appium2.0 之后,这 2 个方法将会被舍弃。 \\\"[Deprecated] \\\'TouchAction\\\' action is deprecated. Please use W3C actions instead.\\\" 1、w3c action 是什么? 在 w3c 的 actions 当中,将输入源

    2024年02月11日
    浏览(23)
  • 二、springboot集成CAS客户端实现单点登录

    pom中引入依赖 yml中添加cas配置 读取CAS相关配置 cas配置类 单点登录接口demo 访问loingCas接口时,若未在CASserver登录,则会被拦截跳转到CAS的登陆页面,登陆成功后放行继续访问loginCas接口

    2024年02月15日
    浏览(41)
  • Springboot实现简单JWT登录鉴权

    登录需要鉴权是为了保护系统的安全性和用户的隐私。在一个 Web 应用中,用户需要提供一定的身份信息(例如用户名和密码)进行登录,登录后系统会为用户生成一个身份令牌(例如 JWT Token)来标识用户的身份。 鉴权的主要目的是确保只有经过身份验证的用户才能访问系统

    2024年02月13日
    浏览(22)
  • 【微服务】springboot 整合mysql实现版本管理通用解决方案

    目录 一、前言 1.1 单独执行初始化sql 1.2 程序自动执行 二、数据库版本升级管理问题

    2024年02月13日
    浏览(25)
  • springBoot JWT实现websocket的token登录拦截认证

    功能:所有关于websocket的请求必须登录,实现websocket需要登录后才可使用,不登录不能建立连接。 后台spring security配置添加websocket的请求可以匿名访问,关于websocket的请求不要认证就可以随意访问,去除匿名访问后,前端在与websocket建立链接无法在请求头里直接加入Authoriz

    2024年02月13日
    浏览(38)
  • SpringBoot+CAS整合服务端和客户端实现SSO单点登录与登出快速入门上手

    教学讲解视频地址:视频地址 因为CAS支持HTTP请求访问,而我们是快速入门上手视频,所以这期教程就不教大家如何配置HTTPS了,如果需要使用HTTPS,可以参考其他博客去云服务器申请证书或者使用JDK自行生成一个证书。 下载CAS Server(直接下载压缩包就可以) 这里我们用的是

    2024年02月02日
    浏览(46)
  • 一张流程图带你学会SpringBoot结合JWT实现登录功能

    🧑‍💻作者名称:DaenCode 🎤作者简介:啥技术都喜欢捣鼓捣鼓,喜欢分享技术、经验、生活。 😎人生感悟:尝尽人生百味,方知世间冷暖。 📖所属专栏:SpringBoot实战 JWT(JsonWebToken)是 一种轻量级的跨域身份验证解决方案 。通常被用于无状态身份验证机制,将用户信息签名

    2024年02月11日
    浏览(70)
  • SpringBoot整合Mybatis-Plus、Jwt实现登录token设置

    Spring Boot整合Mybatis-plus实现登录常常需要使用JWT来生成用户的token并设置用户权限的拦截器。本文将为您介绍JWT的核心讲解、示例代码和使用规范,以及如何实现token的生成和拦截器的使用。 一、JWT的核心讲解 JWT(JSON Web Token)是一种基于JSON的,用于在网络上安全传输信息的

    2024年02月02日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包