IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> SpringSecurity+JWT实现 -> 正文阅读

[网络协议]SpringSecurity+JWT实现

SpringSecurity+JWT

JWT认证和Session认证

参考资料:什么是 JWT – JSON WEB TOKEN - 简书 (jianshu.com)

JWT:JSON WEB TOKEN

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

传统Session认证存在的问题

因为http协议本身是无状态协议,即时用户第一次请求时提供了用户名和密码进行认证,第二次请求时仍需要提供用户名和密码,因为对后端来说并不能知道是哪个用户发出的请求。

传统的Session认证就是用户登录成功后,将用户信息存放在后台session中,前端下次请求时需要在cookie中携带对应的sessionid才能表明其已经登录过。

问题

  • 因为用户信息需要存放在后端的session中(后端内存),当用户信息增多时服务端内存开销变大

  • 用户认证后信息保存在当前服务器中,但对于分布式应用来说,其他服务器服务器中是没有该服务器中的session信息的

  • CSRF跨域问题:session认证的结果被存放在cookie中,攻击者是可以在你访问网站时获取到你的cookie并且伪造请求去请求信息

    (49条消息) 了解cookie以及cookie跨站点伪造攻击(CSRF)_周刚的专栏-CSDN博客

JWT认证方式

token的认证方式下,服务端不需要为用户保存认证信息

大致流程

  • 用户账号密码请求服务器
  • 服务器认证通过
  • 生成一个token(jwt),该jwt是加密的,需要服务端有对于的私钥才能解密。
  • 前端存储用户token,并在后续请求头中携带token
  • 服务器验证token的值并解密取出token中的信息再次生成security中需要的authentication对象。

具体实现

因为Security的认证流程就是一串和UsernamePasswordFilter类似的过滤器在过滤器链上拦截请求然后认证,认证通过就返回认证信息即可,我们可以模拟UsernamePasswordFilter写一个自己的UsernamePasswordFilter

1. 配置JWT认证异常处理器

package com.sy.security.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.sy.security.domain.pojo.Result;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 没有访问权限
 */
public class JWTAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        String reason = "统一处理,原因:" + e.getMessage();
        httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(Result.failure(reason)));
    }
}

2. 生成JWT的工具类

pom.xml中导入依赖

<!--JWT-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

创建JwtTokenUtils类

package com.sy.security.util;

import com.sy.security.domain.pojo.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;

@Slf4j
public class JwtTokenUtils {

    public static final String TOKEN_HEADER = "Authorization";
    public static final String TOKEN_PREFIX = "Bearer ";
    //密钥自定义
    private static final String SECRET = "xxx";
    //签授人自定义
    private static final String ISS = "Shenyang";

    // 过期时间是3600秒,既是1个小时 单位秒
    private static final long EXPIRATION = 7200L;

    // 选择了记住我之后的过期时间为7天 单位秒
    private static final long EXPIRATION_REMEMBER = 604800L;

    // 创建token
    public static String createToken(String subject,HashMap<String,Object> claims,boolean isRememberMe) {
        long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .setClaims(claims)
                .setIssuer(ISS)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .compact();
    }

    // 从token中获取用户名
    public static String getUsername(String token){
        return getTokenBody(token).getSubject();
    }


    // 是否已过期
    public static boolean isExpiration(String token) {
        try {
            return getTokenBody(token).getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return true;
        }
    }
	
    private static Claims getTokenBody(String token){
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }
    //解析token并获取我们存放的信息
    public static Object getClaims(String token,String claim){
        return getTokenBody(token).get(claim);
    }

    public static String createJwt(User user,Integer rememberMe) throws IOException {
        boolean isRemember = rememberMe == 1;
        Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
        String role = null;
        for (GrantedAuthority authority : authorities) {
            role = authority.getAuthority();
        }
        //在token中放入要保存的用户信息
        //这里存放的保存的信息就是用户二次请求时我们能获取到的信息
        HashMap<String,Object> claims = new HashMap<>();
        claims.put("USER_ROLE",role);
        claims.put("USER_NAME",user.getUsername());
        claims.put("USER_REAL_NAME",user.getRealName());
        //创建token并返回
        return JwtTokenUtils.createToken(user.getUsername(),claims , isRemember);
    }
}
  1. JWT异常类

    package com.sy.security.handler.exception;
    
    /**
     * 自定义异常
     */
    public class TokenIsExpiredException extends Exception {
    
    
        public TokenIsExpiredException() {
        }
    
        public TokenIsExpiredException(String message) {
            super(message);
        }
    
        public TokenIsExpiredException(String message, Throwable cause) {
            super(message, cause);
        }
    
        public TokenIsExpiredException(Throwable cause) {
            super(cause);
        }
    
        public TokenIsExpiredException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
            super(message, cause, enableSuppression, writableStackTrace);
        }
    }
    
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-10-08 12:07:15  更:2021-10-08 12:08:32 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:34:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码