spring security 超详细使用教程(接入springboot、前后端分离)

Spring Security 是一个强大且可扩展的框架,用于保护 Java 应用程序,尤其是基于 Spring 的应用。它提供了身份验证(验证用户身份)、授权(管理用户权限)和防护机制(如 CSRF 保护和防止会话劫持)等功能。

Spring Security 允许开发者通过灵活的配置实现安全控制,确保应用程序的数据和资源安全。通过与其他 Spring 生态系统的无缝集成,Spring Security 成为构建安全应用的理想选择。

核心概念

  • 身份验证 (Authentication): 验证用户的身份(例如,用户名/密码)。
  • 授权 (Authorization): 确定用户是否有权限访问特定资源。
  • 安全上下文 (Security Context): 存储已认证用户的详细信息,应用程序中可以访问。

1、准备工作

1.1 引入依赖

当我们引入 security 依赖后,访问需要授权的 url 时,会重定向到 login 页面(security 自己创建的),login 页面需要账号密码,账号默认是 user, 密码是随机的字符串,在spring项目的输出信息中

  • spring-boot-starter-security
    在这里插入图片描述

    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
        <version>3.3.4</version>
    </dependency>
    
  • jjwt-api
    在这里插入图片描述

    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.12.6</version>
    </dependency>
    
  • jjwt-impl
    在这里插入图片描述

    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.12.6</version>
        <scope>runtime</scope>
    </dependency>
    
  • jjwt-jackson
    在这里插入图片描述

    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.12.6</version>
        <scope>runtime</scope>
    </dependency>
    

一般我们会创建一个 SecurityConfig 类,来管理我们所有与 security 相关的配置。(我们讲的是 security 5.7 版本之后的配置方法,之前的方法跟现在不太一样)

@Configuration
@EnableWebSecurity		// 该注解启用 Spring Security 的 web 安全功能。
public class SecurityConfig {
   

}

下面的都要写到 SecurityConfig 类中

1.2 用户认证的配置

基于内存的用户认证

通过 createUser , manager 把用户配置的账号密码添加到spring的内存中, InMemoryUserDetailsManager 类中有一个 loadUserByUsername 的方法通过账号(username)从内存中获取我们配置的账号密码,之后调用其他方法来判断前端用户输入的密码和内存中的密码是否匹配。

@Bean
public UserDetailsService userDetailsService() {
   
	// 创建基于内存的用户信息管理器
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

	
    manager.createUser(
	    // 创建UserDetails对象,用于管理用户名、用户密码、用户角色、用户权限等内容
	    User.withDefaultPasswordEncoder().username("user").password("user123").roles("USER").build()
    ); 		
    // 如果自己配置的有账号密码, 那么上面讲的 user 和 随机字符串 的默认密码就不能用了
    return manager;
}

当我们点进 InMemoryUserDetailsManager 中 可以发现它实现了 UserDetailsManagerUserDetailsPasswordService 接口,其中 UserDetailsManager 接口继承的 UserDetailsService 接口中就有 loadUserByUsername 方法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

基于数据库的用户认证

上面讲到,spring security 是通过 loadUserByUsername 方法来获取 User 并用这个 User 来判断用户输入的密码是否正确。所以我们只需要继承 UserDetailsService 接口并重写 loadUserByUsername 方法即可

下面的样例我用的 mybatis-plus 来查询数据库中的 user, 然后通过当前查询到的 user 返回特定的 UserDetails 对象

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
   

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
   
        QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
        queryWrapper.eq("username", username);	// 这里不止可以用username,你可以自定义,主要根据你自己写的查询逻辑
        User user = userMapper.selectOne(queryWrapper);
        if (user == null) {
   
            throw new UsernameNotFoundException(username);
        }
        return new UserDetailsImpl(user);	// UserDetailsImpl 是我们实现的类
    }
}

UserDetailsImpl 是实现了 UserDetails 接口的类。UserDetails 接口是 Spring Security 身份验证机制的基础,通过实现该接口,开发者可以定义自己的用户模型,并提供用户相关的信息,以便进行身份验证和权限检查。

@Data
@AllArgsConstructor
@NoArgsConstructor	// 这三个注解可以帮我们自动生成 get、set、有参、无参构造函数
public class UserDetailsImpl implements UserDetails {
   

    private User user;	// 通过有参构造函数填充赋值的

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
   
        return List.of();
    }

    @Override
    public String getPassword() {
   
        return user.getPassword();
    }

    @Override
    public String getUsername() {
   
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
     // 检查账户是否 没过期。
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
      // 检查账户是否 没有被锁定。
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
     //检查凭据(密码)是否 没过期。
        return true;
    }

    @Override
    public boolean isEnabled() {
       // 检查账户是否启用。
        return true;
    }
    
    // 这个方法是 @Data注解 会自动帮我们生成,用来获取 loadUserByUsername 中最后我们返回的创建UserDetailsImpl对象时传入的User。
    // 如果你的字段包含 username和password 的话可以用强制类型转换, 把 UserDetailsImpl 转换成 User。如果不能强制类型转换的话就需要用到这个方法了
	public User getUser() {
   	
	        return user;	
	    }
	}

1.3 基本的配置

下面这个是 security 的默认配置。我们可以修改并把它加到spring容器中,完成我们特定的需求。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
   
    http
            // 开启授权保护
            .authorizeHttpRequests(authorize -> authorize
                    // 不需要认证的地址有哪些
                    .requestMatchers("/blog/**", "/public/**", "/about").permitAll()	
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值