当前位置: 首页 > news >正文

Springboot集成SpringSecurity的介绍及使用

文章目录

  • SpringSecurity简介
  • SpringSecurity原理
    • 1. 认证流程
    • 2. 关键组件
    • 3. 配置示例
  • 项目示例
    • pom.xml文件
    • application.yml
    • 添加springsecurity配置类
    • 请求过滤
    • 实现UserDetailsService
  • 总结

SpringSecurity简介

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

SpringSecurity原理

在Spring Security中,认证(Authentication)是安全框架的核心功能之一,其目的是验证用户身份。Spring Security提供了强大的机制来处理用户认证,主要包括以下几个方面:

1. 认证流程

Spring Security的认证流程大致如下:

  • 请求到达:用户通过HTTP请求访问受保护的资源。
  • 认证过滤器:Spring Security的过滤器链开始工作,首先到达UsernamePasswordAuthenticationFilter(或其他类型的认证过滤器,如BasicAuthenticationFilter等)。
  • 提取凭证:过滤器从请求中提取认证信息(如用户名和密码)。
  • 用户详情服务:使用提取的凭证,通过UserDetailsService接口的实现来加载用户详细信息(如用户名、密码、权限等)。
  • 验证:通过AuthenticationProvider验证用户凭证的正确性。
  • 创建认证对象:如果凭证验证成功,创建一个Authentication对象,其中包含用户信息和权限。
  • 安全上下文持有器:将Authentication对象存储在SecurityContextHolder中,以便后续的请求都能访问到当前认证的用户信息。
  • 授权:根据用户的权限,决定是否允许访问资源。
  • 响应请求:如果用户被授权访问,则返回请求的资源;否则返回未授权响应。

2. 关键组件

UserDetailsService:这是一个接口,用于加载用户特定数据。通常与数据库交互来获取用户信息。

@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserRepository userRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userRepository.findByUsername(username);if (user == null) {throw new UsernameNotFoundException("User not found");}return new User(user.getUsername(), user.getPassword(), getAuthorities(user)); // 实现细节可能有所不同}
}

AuthenticationProvider:这是一个接口,用于验证用户的凭证。Spring Security提供了多种实现,如DaoAuthenticationProvider,它结合了UserDetailsService和密码编码器(如BCrypt)。

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

PasswordEncoder:用于对用户密码进行编码,增强安全性。Spring Security提供了多种编码器实现,如BCrypt。

@Bean
public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();
}

3. 配置示例

在Spring Security配置中,你可以使用Java配置或XML配置来设置这些组件。以下是使用Java配置的示例:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Autowiredprivate PasswordEncoder passwordEncoder;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder);}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/public/**").permitAll() // 公开的URL路径.anyRequest().authenticated() // 其他所有请求都需要认证.and().formLogin() // 启用表单登录功能.and().httpBasic(); // 启用HTTP Basic认证方式}
}

项目示例

pom.xml文件

<?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.3.12.RELEASE</version><relativePath /></parent><groupId>com.cy</groupId><artifactId>springboot-springsecurity</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-springsecurity</name><description>Spring Boot整合Spring Security案例项目</description><properties><java.version>1.8</java.version><mysql.version>8.0.28</mysql.version><druid.version>1.1.21</druid.version><lombok.version>1.18.22</lombok.version><mybatis.version>2.2.2</mybatis.version><mybatis-plus.version>3.4.0</mybatis-plus.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--validation--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><!--spring security--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version></dependency><!--druid--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><!--swagger2依赖--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>2.9.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

application.yml

server:port: 8083servlet:context-path: /spring:datasource:username: db_userpassword: db_passwordurl: jdbc:mysql://localhost:3306/spring-securitydriver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcemybatis-plus:mapper-locations: classpath:mapper/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpltype-aliases-package: com.cy.entitylogging:level:springfox: errorcn.edu.sgu.www.security: debug

项目开发过程因为配置错误登录页面、登录路径,导致项目直接进入loadUserByUsername方法,不进入自己编写的登录接口,在配置时需要注意

添加springsecurity配置类

package com.cy.config;import com.cy.security.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/*** Spring Security配置类* @version 1.0*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new PasswordEncoder() {@Overridepublic String encode(CharSequence charSequence) {return (String) charSequence;}@Overridepublic boolean matches(CharSequence charSequence, String s) {return charSequence.equals(s);}};}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {// 禁用防跨域攻击http.csrf().disable();http.sessionManagement().maximumSessions(1).expiredUrl( "/user/login");http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);}@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}
}

请求过滤

JwtAuthenticationTokenFilter.java

package com.rc.security;import com.rc.entity.SysUser;
import com.rc.service.SysUserService;
import com.rc.utils.JwtUtil;import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Autowiredprivate SysUserService sysUserService;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException {String token = request.getHeader("Authorization");if (!StringUtils.hasText(token)) {filterChain.doFilter(request, response);return;}String userid;try {Claims claims = JwtUtil.parseJWT(token);userid = claims.getSubject();} catch (Exception e) {throw new RuntimeException(e);}SysUser user = sysUserService.getById(userid);if (user == null) {throw new RuntimeException("用户名未登录");}UserDetails loginUser = userDetailsService.loadUserByUsername(user.getUsername());UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(loginUser, null, null);// 如果是有效的jwt,那么设置该用户为认证后的用户SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}
}

实现UserDetailsService

package com.cy.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cy.dto.UserLoginDTO;
import com.cy.entity.User;
import com.cy.mapper.UserMapper;
import com.cy.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService, UserDetailsService {@Autowiredprivate AuthenticationManager authenticationManager;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 查询用户信息User user = selectByUsername(username);if (user == null) {throw new BadCredentialsException("登录失败,用户名不存在!");} else {List<String> permissions = selectPermissions(username);return org.springframework.security.core.userdetails.User.builder().username(user.getUsername()).password(user.getPassword()).accountExpired(false).accountLocked(false).disabled(!user.isEnable()).credentialsExpired(false).authorities(permissions.toArray(new String[] {})).build();}}/*** 通过用户名查询用户信息* @param username 用户名* @return User*/private User selectByUsername(String username) {QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.eq("username", username);List<User> list = list(queryWrapper);if (list.size() == 1) {return list.get(0);}return null;}/*** 通过用户名查询用户权限* @param username 用户名* @return List<String>*/private List<String> selectPermissions(String username) {if (username == null) {throw new RuntimeException("用户名不能为空");}List<String> permissions = new ArrayList<>();permissions.add("/user/login");permissions.add("/user/logout");return permissions;}@Overridepublic void login(UserLoginDTO userLoginDTO) {Authentication authentication = new UsernamePasswordAuthenticationToken(userLoginDTO.getUsername(),userLoginDTO.getPassword());authenticationManager.authenticate(authentication);}@Overridepublic void logout() {// todo}@Overridepublic User selectById(String userId) {return this.getById(userId);}}

项目地址:https://gitee.com/wangcheng626/spring-security-demo

总结

Spring Security通过上述流程和组件,提供了一套灵活且强大的用户认证机制。开发者可以根据需要自定义用户加载逻辑、密码编码策略等,从而满足不同的安全需求。通过合理配置,可以有效地保护Web应用免受未授权访问的威胁。

http://www.lryc.cn/news/589007.html

相关文章:

  • AR眼镜颠覆医疗:精准手术零误差
  • 1.1 前端-vue3项目的创建
  • vscode 打开c++文件注释乱码
  • mysql 索引失效的场景
  • 将 Vue 3 + Vite + TS 项目打包为 .exe 文件
  • SpringBoot 实现 Redis读写分离
  • 编译原理第一到三章(知识点学习/期末复习/笔试/面试)
  • 第三章 OB SQL 引擎高级技术
  • HR数字化转型:3大痛点解决方案与效率突破指南
  • B/S架构系统角色与对应协议详解
  • AAAI-2025 | 同济大学面向嘈杂环境的音频视觉导航!BeDAViN:大规模音频-视觉数据集与多声源架构研究
  • HCIA第三次综合实验:VLAN
  • iOS高级开发工程师面试——常见第三方框架架构设计
  • ESP32S3+VSCode+PlatformIO+Arduino+Freertos开发入门指南:基于Arduino框架的应用开发全流程
  • 基于LAMP环境的校园论坛项目
  • 新手向:Python数据处理Excel报表自动化生成与分析
  • 剑指offer62_骰子的点数
  • 为什么市场上电池供电的LoRa DTU比较少?
  • [Pytest][Part 5]单条测试和用例集测试
  • MMYSQL刷题
  • CAU数据挖掘 第五章 聚类问题
  • 【canal+mysql+example+数据验证测试】
  • Python 内置函数random
  • 行为模式-状态模式
  • 小智完整MCP交互流程(以调节音量为例)
  • 网络安全职业指南:探索网络安全领域的各种角色
  • 使用llama-factory进行qwen3模型微调
  • elasticsearch 下载/安装
  • MaxKB使用笔记【持续ing】
  • python+selenium UI自动化初探