SpringSecurity中的授权、认证

798 阅读8分钟

在Spring Security中,进行认证(Authentication)和授权(Authorization)通常涉及重写或实现几个关键的类和接口。下面是一些核心组件和几个简单的代码示例。

1.分别讨论

UserDetailsService 接口在 Spring Security 中扮演着非常重要的角色。它是用户认证过程的核心部分,负责根据用户名获取用户的详细信息。这些信息包括用户名、密码、权限等,都是 Spring Security 进行认证和授权决策所必需的。

UserDetailsService 接口概述

  • 主要职责:它的主要职责是提供一种从任何数据源(如数据库、LDAP、文件等)加载用户信息的方式。
  • 方法:该接口只有一个方法 loadUserByUsername(String username),它接受一个用户名作为参数,并返回一个 UserDetails 实例。

实现 UserDetailsService

实现 UserDetailsService 接口通常涉及以下步骤:

  • 创建实现类:创建一个类实现 UserDetailsService 接口。
  • 加载用户信息:在 loadUserByUsername 方法中,根据用户名从你的数据源(例如数据库)中加载用户。
  • 构建 UserDetails:根据用户信息构建并返回一个 UserDetails 实例。这通常涉及设置用户名、密码和授权(GrantedAuthority)。

示例代码

下面是一个简单的 UserDetailsService 实现示例,假设用户信息存储在数据库中:

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
​
public class CustomUserDetailsService implements UserDetailsService {
​
    private JdbcTemplate jdbcTemplate;
​
    @Autowired
    public CustomUserDetailsService(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
​
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDetails user = jdbcTemplate.queryForObject(
            "SELECT username, password, enabled FROM users WHERE username = ?",
            new Object[]{username},
            new RowMapper<UserDetails>() {
                @Override
                public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
                    String username = rs.getString("username");
                    String password = rs.getString("password");
                    boolean enabled = rs.getBoolean("enabled");
                    return User.withUsername(username).password(password).disabled(!enabled).build();
                }
            });
        
        if (user == null) {
            throw new UsernameNotFoundException("User not found: " + username);
        }
        
        return user;
    }
}

UserDetails 接口在 Spring Security 中定义了 Spring 需要的用户信息,这些信息用于认证和授权过程。一个 UserDetails 实例代表了一个用户的详细信息,包括用户名、密码、账户状态和所拥有的权限。

UserDetails 接口概述

  • 主要用途:它用于封装认证用户的信息,如用户名、密码和授权(GrantedAuthority)。

  • 关键方法UserDetails 提供了多个方法来获取用户的安全详情,包括:

    • getUsername(): 获取用户名。
    • getPassword(): 获取用户密码。
    • getAuthorities(): 获取用户拥有的权限。
    • isAccountNonExpired(): 账户是否未过期。
    • isAccountNonLocked(): 账户是否未被锁定。
    • isCredentialsNonExpired(): 凭证(密码)是否未过期。
    • isEnabled(): 账户是否启用。

实现 UserDetails

实现 UserDetails 接口通常涉及以下步骤:

  • 创建用户实体类:创建一个类,通常是一个实体类(如果使用 ORM 框架如 Hibernate)。
  • 实现 UserDetails 接口:实现接口中的所有方法。
  • 添加属性和逻辑:根据您的应用需求,添加必要的属性(如 username, password, authorities 等)和方法的实现逻辑。

示例代码

下面是一个 UserDetails 实现的简单示例:

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
​
public class CustomUserDetails implements UserDetails {
​
    private String username;
    private String password;
    private boolean enabled;
    private Collection<? extends GrantedAuthority> authorities;
​
    // 构造函数、getter 和 setter 省略
​
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }
​
    @Override
    public String getPassword() {
        return password;
    }
​
    @Override
    public String getUsername() {
        return username;
    }
​
    @Override
    public boolean isAccountNonExpired() {
        return true; // 可以添加逻辑来判断账户是否过期
    }
​
    @Override
    public boolean isAccountNonLocked() {
        return true; // 可以添加逻辑来判断账户是否被锁定
    }
​
    @Override
    public boolean isCredentialsNonExpired() {
        return true; // 可以添加逻辑来判断凭证(密码)是否过期
    }
​
    @Override
    public boolean isEnabled() {
        return enabled; // 或者根据业务需求提供逻辑
    }
}

GrantedAuthority 接口在 Spring Security 中代表了授权信息,是处理用户权限和角色的基本单位。它主要用于表达赋予给特定用户的权限或角色,作为用户认证和授权过程的关键部分。

GrantedAuthority 接口概述

  • 用途:在 Spring Security 中,GrantedAuthority 对象通常代表用户的安全权限。这些权限可以是角色(如 ROLE_ADMIN)或更细粒度的权限(如 READ_PRIVILEGE)。
  • 方法:该接口包含一个方法 getAuthority(),用于返回一个 String,表示权限或角色的名称。

如何使用 GrantedAuthority

  • UserDetails 实现中提供权限:在实现 UserDetails 接口的类中,getAuthorities() 方法需要返回一个 GrantedAuthority 对象的集合,表明该用户所拥有的权限。
  • 定义角色或权限:可以定义一些常用的角色或权限常量,如 ROLE_USER, ROLE_ADMIN, READ_PRIVILEGE, WRITE_PRIVILEGE 等。
  • 权限检查:Spring Security 在决策管理器中使用这些权限来决定是否授权用户进行特定操作。

示例代码

以下是一个简单的 GrantedAuthority 实现示例:

import org.springframework.security.core.GrantedAuthority;
​
public class CustomGrantedAuthority implements GrantedAuthority {
​
    private String authority;
​
    public CustomGrantedAuthority(String authority) {
        this.authority = authority;
    }
​
    @Override
    public String getAuthority() {
        return this.authority;
    }
}

AuthenticationProvider 接口在 Spring Security 中扮演着自定义认证逻辑的关键角色。它使开发者能够插入自己的认证机制,而不仅仅依赖于 Spring Security 默认提供的认证方式。这使得 Spring Security 变得极为灵活,能够适应各种复杂和特定的安全需求。

AuthenticationProvider 接口概述

  • 主要职责AuthenticationProvider 负责处理 Authentication 请求。在这个过程中,它会决定是否可以对提交的 Authentication 对象进行认证,并返回一个已经过认证的 Authentication 实例(如果认证成功的话)。
  • 方法:它定义了一个主要的方法 authenticate(Authentication authentication),用于尝试认证传递的 Authentication 对象。还有一个 supports(Class<?> authentication) 方法,用于判断该 AuthenticationProvider 是否能够处理指定 Authentication 类型的认证请求。

实现 AuthenticationProvider

实现 AuthenticationProvider 通常涉及以下步骤:

  • 创建实现类:创建一个类实现 AuthenticationProvider 接口。
  • 实现认证逻辑:在 authenticate 方法中,添加自定义的认证逻辑,如检查用户名和密码、进行额外的安全检查等。
  • 支持的认证类型:在 supports 方法中,返回此认证提供者是否支持特定 Authentication 类型的布尔值。

示例代码

下面是一个简单的 AuthenticationProvider 实现示例:

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.BadCredentialsException;
​
public class CustomAuthenticationProvider implements AuthenticationProvider {
​
    private UserDetailsService userDetailsService;
​
    public CustomAuthenticationProvider(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
​
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
​
        UserDetails user = userDetailsService.loadUserByUsername(username);
​
        if (user != null && user.getPassword().equals(password)) {
            return new UsernamePasswordAuthenticationToken(username, password, user.getAuthorities());
        } else {
            throw new BadCredentialsException("External system authentication failed");
        }
    }
​
    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

WebSecurityConfigurerAdapter 是 Spring Security 中用于配置安全策略的核心类之一。通过继承这个类并重写其方法,开发者可以定制应用程序的安全配置,包括指定哪些 URL 路径应该被保护、如何保护它们、设置登录和登出行为等。

WebSecurityConfigurerAdapter 类概述

  • 用途:该类提供了一个便利的方式来使用 Java 配置来定制安全设置。

  • 配置:主要通过重写它的几个关键方法来实现安全配置:

    • configure(HttpSecurity http): 用于配置如何通过拦截器保护请求。
    • configure(AuthenticationManagerBuilder auth): 用于配置 AuthenticationManager,定义用户详细信息服务(UserDetailsService)和密码编码器(PasswordEncoder)。
    • configure(WebSecurity web): 用于配置 WebSecurity,主要用于配置不需要安全保护的路径(如静态资源)。

实现 WebSecurityConfigurerAdapter

实现 WebSecurityConfigurerAdapter 通常涉及以下步骤:

  • 创建配置类:创建一个类继承自 WebSecurityConfigurerAdapter
  • 配置 HTTP 安全:重写 configure(HttpSecurity http) 方法,设置不同 URL 的安全策略,如何认证和授权。
  • 配置认证管理器:如果需要,重写 configure(AuthenticationManagerBuilder auth) 方法,设置用户认证的具体逻辑。

示例代码

以下是一个 WebSecurityConfigurerAdapter 的简单实现示例,演示了基本的安全配置:

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
​
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
​
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/", "/home").permitAll() // 允许所有用户访问 "/" 和 "/home"
                .antMatchers("/admin/**").hasRole("ADMIN") // "/admin/**" 路径只允许 ADMIN 角色用户访问
                .anyRequest().authenticated() // 其他所有路径都需要认证
            .and()
            .formLogin()
                .loginPage("/login") // 自定义登录页面
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
}

2.完整总结

完整总结一下 UserDetailsService, UserDetails, GrantedAuthority, AuthenticationProvider, 和 WebSecurityConfigurerAdapter 在 Spring Security 中配合使用的一个完整的认证和授权过程。

1. UserDetailsServiceUserDetails

这两部分通常一起工作以提供用户信息。UserDetailsService 接口的实现负责从数据源(如数据库)获取用户信息,而 UserDetails 接口的实现则用于封装这些信息。

  • UserDetailsService:实现此接口以提供一个方法 (loadUserByUsername),用于根据用户名查找用户。这个方法返回一个 UserDetails 实例。
  • UserDetails:这是一个领域对象,包含了用户的信息,如用户名、密码和权限(GrantedAuthority 的集合)。

2. GrantedAuthority

  • GrantedAuthority:表示用户的权限。在 UserDetails 实现中,你将定义用户所拥有的权限。这些权限将被 Spring Security 用于决定用户是否有权访问特定资源。

3. AuthenticationProvider

  • AuthenticationProvider:实现此接口以提供自定义的认证逻辑。它使用 UserDetailsService 来获取用户信息,并执行例如密码检查的认证逻辑。

4. WebSecurityConfigurerAdapter

  • WebSecurityConfigurerAdapter:这个类用于配置整个应用的安全策略。你将在这里配置哪些URL需要保护,哪些不需要,设置登录和登出行为等。

配合使用的流程

  • 用户信息获取:当用户尝试登录时,Spring Security 调用你的 UserDetailsService 实现来加载用户的详细信息。
  • 认证过程:然后,Spring Security 使用 AuthenticationProvider 实现对用户提供的凭据(如用户名和密码)进行认证。AuthenticationProvider 会使用 UserDetailsService 获取的 UserDetails 对象来检查用户的详细信息。
  • 权限分配:如果用户成功通过认证,UserDetails 对象中的 GrantedAuthority 集合将被用于确定用户的权限。
  • 安全配置应用:在 WebSecurityConfigurerAdapter 实现中定义的配置决定了哪些URL需要认证,哪些权限(角色)允许访问特定资源。
  • 访问控制:当用户尝试访问应用中的资源时,Spring Security 根据在 WebSecurityConfigurerAdapter 中定义的安全策略,以及用户的 UserDetails 中的权限来决定是否允许该访问。