【Spring Security系列】Spring Security整合JWT:构建安全的Web应用

前言

在企业级开发或者我们自己的课程设计中,确保用户数据的安全性和访问控制非常重要。而Spring Security和JWT是都两个强大的工具,它俩结合可以帮助我们实现这一目标。

Spring Security提供了全面的安全功能,而JWT则是一种用于身份验证的令牌机制。
在这里插入图片描述

JWT简单介绍

前面两个章节介绍过了Spring Security,这里就不再赘述了!!!

JWT是一种轻量级的身份验证和授权机制,通过发送包含用户信息的加密令牌来实现身份验证。这个工具我们在前面的文章中也提起过。

整合步骤与代码实现

目前大部分项目,大多数是使用前后端分离的模式。前后端分离的情况下,我们使用SpringSecurity解决权限问题的最常见的方案就是SpringSecurity+JWT 。

在这里插入图片描述

添加依赖
首先,我们需要在项目的pom.xml文件中添加Spring Security和JWT的依赖:

<!--JWT-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<!--工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.0.M3</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

接下来配置Spring Security,在Spring Security配置类中,我们自定义用户详情服务和认证管理器,并配置HTTP安全策略:

@Configuration  
@EnableWebSecurity  
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
  
    @Autowired  
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;  
  
    @Autowired  
    private JwtRequestFilter jwtRequestFilter;  
  
    @Autowired  
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {  
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());  
    }  
  
    @Bean  
    @Override  
    public AuthenticationManager authenticationManagerBean() throws Exception {  
        return super.authenticationManagerBean();  
    }  
  
    @Bean  
    public PasswordEncoder passwordEncoder() {  
        return new BCryptPasswordEncoder();  
    }  
  
    @Override  
    protected void configure(HttpSecurity http) throws Exception {  
        http  
            .csrf().disable()  
            .authorizeRequests()  
            .antMatchers("/authenticate").permitAll()  
            .anyRequest().authenticated()  
            .and()  
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)  
            .and()  
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);  
  
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);  
    }   
}

实现JWT生成和验证,我们创建一个JWT工具类,用于生成和解析JWT:

java
@Component  
public class JwtTokenUtil {  
  
    private String secret = "your_secret_key"; // 私钥,用于签名JWT  
  
    public String generateToken(UserDetails userDetails) {  
        Map<String, Object> claims = new HashMap<>();  
        return Jwts.builder()  
                .setClaims(claims)  
                .setSubject(((User) userDetails).getUsername())  
                .setIssuedAt(new Date(System.currentTimeMillis()))  
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期  
                .signWith(SignatureAlgorithm.HS512, secret)  
                .compact();  
    }  
  
    public String getUsernameFromToken(String token) {  
        return getClaimFromToken(token, Claims::getSubject);  
    }  
  
    public Date getExpirationDateFromToken(String token) {  
        return getClaimFromToken(token, Claims::getExpiration);  
    }  
  
    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {  
        final Claims claims = getAllClaimsFromToken(token);  
        return claimsResolver.apply(claims);  
    }  
  
    private Claims getAllClaimsFromToken(String token) {  
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();  
    }  
  
    public boolean validateToken(String token, UserDetails userDetails) {  
        final String username = getUsernameFromToken(token);  
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));  
    }  
  
    private boolean isTokenExpired(String token) {  
        final Date expiration = getExpirationDateFromToken(token);  
        return expiration.before(new Date());

在这里插入图片描述

创建JWT过滤器与认证管理器

为了在用户每次请求时验证JWT,我们需要创建一个自定义的过滤器。同时,我们还需要一个认证管理器来处理用户的登录请求。

我们实现JWT过滤器

@Component  
public class JwtRequestFilter extends OncePerRequestFilter {  
  
    @Autowired  
    private JwtTokenUtil jwtTokenUtil;  
  
    @Autowired  
    private UserDetailsService userDetailsService;  
  
    @Override  
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
            throws ServletException, IOException {  
        final String requestTokenHeader = request.getHeader("Authorization");  
  
        String username = null;  
        String jwtToken = null;  
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {  
            jwtToken = requestTokenHeader.substring(7);  
            try {  
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);  
            } catch (Exception e) {  
                logger.error("Unable to get JWT Token");  
            }  
        }  
  
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {  
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);  
  
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {  
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =  
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));  
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);  
            }  
        }  
  
        filterChain.doFilter(request, response);  
    }  
}

认证管理器,我们创建一个AuthenticationManager的实现来处理用户的登录请求:

@Service  
public class CustomAuthenticationManager implements AuthenticationManager {  
  
    @Autowired  
    private UserDetailsService userDetailsService;  
  
    @Autowired  
    private PasswordEncoder passwordEncoder;  
  
    @Override  
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
        String username = authentication.getName();  
        String password = authentication.getCredentials().toString();  
  
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);  
  
        if (userDetails == null) {  
            throw new BadCredentialsException("User not found");  
        }  
  
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {  
            throw new BadCredentialsException("Wrong password");  
        }  
  
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
    }  
}

创建控制层LoginController

RestController  
@RequestMapping("/security")  
public class AuthenticationController {  
  
    @Autowired  
    private CustomAuthenticationManager authenticationManager;  
  
    @Autowired  
    private JwtTokenUtil jwtTokenUtil;  
  
    @Autowired  
    private UserDetailsService userDetailsService;  
  
    @PostMapping("/login")
    public ResponseEntity<?> createAuthenticationToken(@Valid @RequestBody LoginRequest loginRequest) throws Exception {  
        authenticate(loginRequest.getUsername(), loginRequest.getPassword());  
        final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());  
        final String token = jwtTokenUtil.generateToken(userDetails);  
        return ResponseEntity.ok(new JwtAuthenticationResponse(token));  
    }  
  
    private void authenticate(String username, String password) throws Exception {  
        try {  
            authenticationManager.authenticate(  
                    new UsernamePasswordAuthenticationToken(username, password)  
            );  
        } catch (DisabledException e) {  
            throw new Exception("USER_DISABLED", e);  
        } catch (BadCredentialsException e) {  
            throw new Exception("INVALID_CREDENTIALS", e);  
        }  
    }  
}

使用ApiFox测试

在这里插入图片描述
这样,我们就可以构建一个安全且高效的Web应用了。

小结

Spring Security提供了强大的身份验证和授权功能,而JWT则提供了一种轻量级的令牌机制来验证用户身份。通过结合使用,我们可以实现无缝的用户身份验证和访问控制,然后保护我们应用的数据安全。

文章到这里就先结束了,后续会继续分享相关的知识点。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/563634.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

最新win11配置cuda以及cudnn补丁教程

1、首先使用指令 nvidia-smi 查看电脑支持的**最高cuda**版本&#xff0c;例如&#xff1a;本机 12.2 2、进入CUDA下载cuda安装包 https://developer.nvidia.com/cuda-toolkit-archive 2、点击上方绿色的链接&#xff0c;按照图中序号选择的即可&#xff0c;最后点击下载。 …

学习springcloud中Nacos笔记

一、springcloud版本对应 版本信息可以参考&#xff1a;版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 这里说2022.x 分支对应springboot的版本信息&#xff1a; Spring Cloud Alibaba VersionSpring Cloud VersionSpring Boot Version 2022.0.0.0* Spring Cloud 202…

Unity3D 羊了个羊等游戏工程源码/3D资源 大合集

Unity3D休闲益智游戏工程源码大合集 一、关卡类游戏工程源码二、跑酷类游戏工程源码三、消除合成类游戏工程源码四、棋牌类游戏工程源码五、RPG(角色扮演)类游戏工程源码六、FPS&#xff08;射击&#xff09;类游戏工程源码十、Unity3D工艺仿真六、Unity游戏资源1、Unity3D 吃鸡…

软件杯 深度学习实现语义分割算法系统 - 机器视觉

文章目录 1 前言2 概念介绍2.1 什么是图像语义分割 3 条件随机场的深度学习模型3\. 1 多尺度特征融合 4 语义分割开发过程4.1 建立4.2 下载CamVid数据集4.3 加载CamVid图像4.4 加载CamVid像素标签图像 5 PyTorch 实现语义分割5.1 数据集准备5.2 训练基准模型5.3 损失函数5.4 归…

【C++】C++11 包装器

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 function包装器 fu…

# 从浅入深 学习 SpringCloud 微服务架构(二)模拟微服务环境(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;二&#xff09;模拟微服务环境&#xff08;1&#xff09; 段子手168 1、打开 idea 创建父工程 创建 artifactId 名为 spring_cloud_demo 的 maven 工程。 --> idea --> File --> New --> Project --> Ma…

【Hello算法】 > 第 3 关 >栈与队列

数据结构 之 数组与链表 1 栈 / 栈的常见操作、实现、应用2 队列 /队列的常见操作、实现、应用3 双向队列4 Tips ———————————————————————————————————————————————————————————- ————————————————…

MySQL基础篇总结

参考&#xff1a;黑马程序员MySQL基础视频链接 数据库基本操作 启动与停止 1.第一种方式&#xff1a; 1>以管理员身份运行cmd 2>在命令行窗口中输入: 启动:net start mysql80停止:net stop mysql80 2.第二种方式: 1>WinR快捷方式打开如下&#xff1a; 输入&#…

CompletableFuture编排异步线程

CompletableFuture 是 Java 8 引入的一种新的 Future&#xff0c;设计目的是为了编写非阻塞的异步代码。 传统异步编程方式 传统异步编程方式获得异步任务值&#xff0c;首先我们得通过future task &#xff0c;然后创建一个实现callable内部类&#xff0c;或者通过lambda的表…

Stable Diffusion教程:提示词(模型、插件、安装包可分享)

什么是提示词 文章提到的模型、插件、安装包都可分享&#xff0c;需要的小伙伴文末领取&#xff01; 你可能没写过提示词&#xff0c;但是一定听说过“提示词”这几个字&#xff0c;也大概能知道它的重要性。 没听说过也没关系&#xff0c;下面我就带你认识认识。 提示词就…

ARM_day6:实现字符串数据收发函数的封装

程序代码&#xff1a; uart4.h&#xff1a; #ifndef __UART4_H__ #define __UART4_H__ #include"stm32mp1xx_gpio.h" #include"stm32mp1xx_rcc.h" #include"stm32mp1xx_uart.h" void uart4_config(); void putchar(char dat); char getchar();…

GeoServer 样式指南

毫无疑问,Geoserver 是遵循 OGC 标准共享空间数据的最佳方式之一。 Geoserver 允许我们以 WMS 格式公开数据,通过以栅格(png、jpeg 等)格式显示大量矢量数据,使开发人员和用户的工作变得非常轻松。 本博客介绍了 GeoServer 功能的又一领域,即动态设置数据样式。首先,矢量…

AGM AG32 MCU在汽车UWB应用方案

AG32的汽车UWB应用方案 汽车电子产品的日益成熟&#xff0c;包括ADAS和车载信息娱乐&#xff0c;正在推动对CPLD的需求。例如&#xff0c;利用安装在车上的各种传感器&#xff08;如雷达、摄像头和激光雷达等&#xff09;来感知周围环境&#xff0c;实现实时监测和数据处理。这…

2023年网络安全行业:机遇与挑战并存

2023年全球网络安全人才概况 根据ISC2的《2023年全球网络安全人才调查报告》&#xff0c;全球的网络安全专业人才数量达到了550万&#xff0c;同比增长了8.7%。然而&#xff0c;这一年也见证了网络安全人才短缺达到了历史新高&#xff0c;缺口数量接近400万。尤其是亚太地区&am…

Centos7 的 Open Stack T 版搭建流程 --- (三)配置消息队列

配置消息队列 文章目录 配置消息队列&#xff08;1&#xff09;安装 RabbitMQ 服务并配置新用户权限controller &#xff08;2&#xff09;如何开启图形化&#xff08;拓展&#xff09; &#xff08;1&#xff09;安装 RabbitMQ 服务并配置新用户权限 controller yum install…

【支持CPU机器】一个Python文件搭建你本地图片生成编辑会话系统——TaskMatrix架构解读

一. 先上效果 TaskMatrix通过ChatGPT 和一系列 Visual Foundation 模型&#xff0c;通过聊天实现图片的绘制、问答和编辑。 二. 流程概览 1. 使用者流程 多模型会话上下文用户指令输入到本系统 -> 多模式会话基础模型 -> 理解用户指令上下文&#xff0c;调用API选择器&…

使用ssh无显示器连接树莓派并配置vnc

配置网络连接并开启ssh 使用树莓派官方的烧录工具&#xff1a; Windows下载链接 Linux下载连接 Mac下载链接 Linux还可以使用命令下载 sudo apt install rpi-imager 在烧录前点击小齿轮配置好网络连接和ssh 如果你不知道树莓派的ip地址&#xff0c;可以使用网线连接电脑和…

新品上线!这个真核宿主-宏病毒组分析流程,太合我意了吧!

病毒被称为地球的“暗物质”&#xff0c;亟待研究。近年来&#xff0c;宏病毒组&#xff08;富集/不富集&#xff09;的研究呈爆炸式增长&#xff0c;人们越来越意识到病毒在调节微生态平衡上发挥着重要作用。 对于宏病毒组项目而言&#xff0c;在新病毒的发现、病毒溯源和进化…

FastJson2中FastJsonHttpMessageConverter找不到类问题

问题描述 如果你最近也在升级FastJson到FastJson2版本&#xff0c;而跟我一样也遇到了FastJsonHttpMessageConverter找不到类问题以及FastJsonConfig找不到问题&#xff0c;那么恭喜你&#xff0c;看完本文&#xff0c;安装完fastjson2、fastjson2-extension、fastjson2-exte…

【Linux学习】Linux调试器-gdb使用

这里写目录标题 &#x1f302;背景&#x1f302;gdb使用&#x1f302;指令总结&#xff1a; &#x1f302;背景 程序的发布方式有两种&#xff0c;debug模式和 release模式 其中&#xff0c;debug模式是可以被调试的&#xff0c;到那时release模式是不能被调试的&#xff1b; …