SpringBoot学习笔记(十三:JWT )



在这里插入图片描述

一、JWT简介

JSON Web Token(JWT)是一个开放的标准(RFC 7519),它定义了一个紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。

JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。


1、JWT认证

传统的session认证一般是这样的流程:

  • 1、用户向服务器发送用户名和密码。

  • 2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。

  • 3、服务器向用户返回一个 session_id,写入用户的 Cookie。

  • 4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。

  • 5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。


session认证流程

在这里插入图片描述

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

一种解决方案是 session共享,将session持久化或者存入缓存。各种服务收到请求后,都向持久层或缓存请求数据。这种方案的优点是架构清晰,缺点是工程量比较大。另外,持久层或者缓存万一挂了,就会认证失败。

另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。

JWT认证流程:

  • 1、 用户使用账号和密码发出post请求;
  • 2、 服务器使用私钥创建一个jwt;
  • 3、 服务器返回这个jwt给浏览器;
  • 4、 浏览器将该jwt串在请求头中像服务器发送请求;
  • 5、 服务器验证该jwt;
  • 6、 返回响应的资源给浏览器。


jwt认证流程

在这里插入图片描述


2、JWT的组成


JWT信息

在这里插入图片描述

从上图可以看到,JWT含有三部分:头部(header)、载荷(payload)、签名(signature)。

2.1、头部(header)

JWT的头部有两部分信息:

  • 声明类型,这里是JWT
  • 声明加密的算法,通常直接使用HMAC SHA256

头部示例如下:

{
  "alg": "HS256",
  "typ": "JWT"
}

头部一般使用base64加密(该加密是可以对称解密的),构成了第一部分:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

2.2、载荷(payload)

这部分一般存放一些有效的信息。JWT的标准定义包含五个字段:

  • iss:该JWT的签发者
  • sub: 该JWT所面向的用户
  • aud: 接收该JWT的一方
  • exp(expires): 什么时候过期,这里是一个Unix时间戳
  • iat(issued at): 在什么时候签发的

载荷示例如下:

{
    "iss": "kkjwt",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "www.example.com",
    "sub": "kk"
}


2.3、签名(signature)

Signature 部分是对前两部分的签名,防止数据篡改。

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。


    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。


4、JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

Authorization: Bearer <token>

另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。


二、SpringBoot整合JWT

1、依赖

引入jwt的依赖

        <!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.2</version>
        </dependency>


2、工具类

Jwt工具类进行token的生成和认证:

/**
 * JWT工具类
 * 用于生成和校验token
 */

public class JwtUtil {
    private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
    /**
     * 秘钥
     */
    private static final String SECRET = "my_secret";

    /**
     * 过期时间
     **/
    private static final long EXPIRATION = 1800L;//单位为秒

    /**
     * 生成用户token,设置token超时时间
     */
    public  static String createToken(User user){
        //过期时间
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        String token= JWT.create()
                .withHeader(map)                //添加头部
                //可以把数据存在claim中
                .withClaim("id",user.getId())      //userId
                .withClaim("name",user.getName())
                .withClaim("userName",user.getUserName())
                .withExpiresAt(expireDate)          //超时设置,设置过期的日期
                .withIssuedAt(new Date()) //签发时间
                .sign(Algorithm.HMAC256(SECRET)); //SECRET加密
        return token;
    }

    /**
     * 检验token并解析token
     */
    public static Map<String, Claim> verifyToken(String token){
        DecodedJWT jwt=null;
        try {
            JWTVerifier verifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
            jwt=verifier.verify(token);
        }catch (Exception e){
            logger.error(e.getMessage());
            logger.error("解析编码异常");
        }

        return jwt.getClaims();
    }
}

3、过滤器

JWT过滤器中进行token的校验和判断,,token不合法直接返回,合法则解密数据并把数据放到request中供后续使用。

为了使过滤器生效,需要在启动类添加注解@ServletComponentScan(basePackages = “edu.hpu.filter”):

@WebFilter(filterName = "jwtFilter",urlPatterns = "/secure/*")
public class JwtFilter implements Filter{
    private final Logger logger = LoggerFactory.getLogger(JwtFilter.class);


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;

        response.setCharacterEncoding("UTF-8");
        //获取header里的token
        String token=request.getHeader("authorization");
        if ("OPTIONS".equals(request.getMethod())) {              //除了 OPTIONS请求以外, 其它请求应该被JWT检查
            response.setStatus(HttpServletResponse.SC_OK);
            filterChain.doFilter(request, response);
        }else {
            if (token == null) {
                response.getWriter().write("没有token!");
                return;
            }
        }

        Map<String, Claim> userData = JwtUtil.verifyToken(token);

        if (userData==null){
            response.getWriter().write("token不合法!");
            return;
        }
        Integer id = userData.get("id").asInt();
        String name = userData.get("name").asString();
        String userName = userData.get("userName").asString();
        //拦截器 拿到用户信息,放到request中
        request.setAttribute("id", id);
        request.setAttribute("name", name);
        request.setAttribute("userName", userName);
        filterChain.doFilter(servletRequest,servletResponse);

    }

    @Override
    public void destroy() {

    }
}

4、控制层

  • 登录Controller进行登录操作,登录成功后生产token并返回:
/**
 * 登录Controller
 */

@RestController

public class LoginController {
    private final Logger logger = LoggerFactory.getLogger(LoginController.class);

    //模拟数据库
    static Map<Integer, User> userMap = new HashMap<>();
    static {
        User user1 = new User(1, "zhangsan", "张三", "123456");
        userMap.put(1, user1);
        User user2 = new User(2, "lisi", "李四", "123123");
        userMap.put(2, user2);
    }

    /**
     * 模拟用户登录
     */

    @RequestMapping("/login")
    public String login(User user){
        for (User dbUser : userMap.values()) {
            if (dbUser.getName().equals(user.getName()) && dbUser.getPassword().equals(user.getPassword())) {
                logger.info("登录成功!生成token!");
                String token = JwtUtil.createToken(dbUser);
                return token;
            }
        }
        return "";
    }
}

  • SecureController中的请求会被JWT过滤器拦截,合法后才能访问:
**
 * 需要登录后才可访问
 */
@RestController
public class SecureController {
    private final Logger logger = LoggerFactory.getLogger(SecureController.class);

    /**
     * 查询用户信息,登录后才可访问
     */

    @RequestMapping("/secure/getUserInfo")
    public String getUserInfo(HttpServletRequest request){
        Integer id = (Integer) request.getAttribute("id");
        String name = request.getAttribute("name").toString();
        String userName = request.getAttribute("userName").toString();
        return "当前用户信息id=" + id + ",name=" + name + ",userName=" + userName;
    }
}

三、测试

测试时先访问登录接口,根据用户名和密码生成token,再将token放在请求头里,去访问需要登录才能访问的接口。


1、访问登录接口

直接访问登录接口:http://localhost:8080/login?name=zhangsan&password=123456

登录成功则返回token:

在这里插入图片描述

2、访问需要登录的接口

访问http://localhost:8080/secure/getUserInfo,请求头里需要携带token:

在这里插入图片描述

成功获取用户信息。




本文为学习笔记类博客,学习资料来源见参考!


参考:

【1】:《SpringBoot Vue 全栈开发实战》
【2】:一分钟带你了解JWT认证!
【3】:SpringBoot集成JWT实现权限认证
【4】:使用 JWT 来保护你的 SpringBoot 应用
【5】:基于jwt的token验证
【6】:前后端分离之JWT用户认证
【7】:JSON Web Token 入门教程
【8】:傻傻分不清之 Cookie、Session、Token、JWT

  • 6
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
要在Spring Boot前后端分离中实现JWT单点登录,您可以按照以下步骤进行: 1. 后端实现JWT认证和授权 在后端,您需要使用Spring Security和JWT实现认证和授权。当用户登录成功时,使用JWT生成一个token并将其返回给前端。在后续请求中,前端需要将token作为Authorization Header发送到后端验证用户的身份和权限。 2. 在前端存储JWT token 在前端,您需要将JWT token存储在客户端。您可以使用localStorage或sessionStorage来存储JWT token。 3. 实现单点登录 当用户访问其他资源(另一个前端应用或后端API)时,前端需要检查本地存储的JWT token是否存在。如果存在,则将其发送到后端进行验证。如果JWT token有效,则用户已经登录,可以访问资源。如果JWT token无效,则用户需要重新登录。 4. 共享JWT secret 要实现单点登录,您需要使用相同的JWT secret在所有应用程序之间共享JWT token。秘密应该足够强大,以防止未经授权的访问。 5. 解决JWT token过期问题 JWT token有一个过期时间,当过期时,用户需要重新登录。要解决这个问题,您可以使用refresh token或者每次用户访问时都重新生成一个新的JWT token。 以上是实现Spring Boot前后端分离的JWT单点登录的大致步骤。实现单点登录需要考虑安全性、性能和用户体验等方面的问题。因此,您需要根据实际情况进行自定义和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三分恶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值