《Spring Security》第五章 SpringSecurity 中的核心组件

第五章 SpringSecurity 中的核心组件

通过前四章简单介绍了下 SpringSecurity 的命名空间配置和Java 配置,已经可以简单一个基于 Spring Security 的应用程序。

下面,将会研究其中一些在整个框架中使用的中心接口、类和抽象概念,了解它们如何协同工作,以支持 Spring Security 中的身份验证和访问控制。

一、SecurityContextHolder

Spring-Security-Core 中对 SecurityContextHolder 的介绍如下:

Associates a given {@link SecurityContext} with the current execution thread.
将给定的{@link SecurityContext}与当前执行线程关联。

SecurityContextHolder 用于存储应用程序当前Security 上下文的地方,其中包含当前使用应用程序的主体的细节。

默认情况下,SecurityContextHolder 使用ThreadLocal 来存储这些细节,如果在处理完当前主体的请求之后清除线程,那么这种方式就是非常安全的。

一般没有必要修改SecurityContextHolder 的默认值,但是 SpringSecurity 提供了修改的途径。

二、SecurityContext

Interface defining the minimum security information associated with the current thread of execution.

与安全信息相关的抽象接口,用来获取或设置 Authentication

三、Authentication

实现了 Principal 与序列化的接口,包含了当前请求的主体信息。通常是保存在 SecurityContext 中。

获取方法如下:

public String getUsernameBySecurityContext(){
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    String username;
    if(prinicpal instanceof UserDetails){
        username = ((UserDetails)principal).getUsername();

    }else {
        username = principal.toString();
    }
    return username;
}

四、GrantedAuthority

除了用户主体之外,身份验证还提供了一额外的方法 getAuthorities(),该方法返回一个 GrantedAuthority 对象数组。

GrantedAuthority 通常为“角色”,用于配置 web授权、方法授权、域对象授权等。该属性通常由 UserDetailsService 加载给 UserDetails。

如果一个用户有几千个这种权限,内存的消耗将会是非常巨大的。

五、UserDetails

从上面代码可以看出 Authentication 在大部分情况下是可以直接转换为 UserDetails 对象的。他表示一个主体,可以看做是用户数据库与 SecurityContextHolder 之间的适配器。

UserDetails 是 SpringSecurity 的核心接口,可以通过继承 UserDetails 来实现自定义的方法与属性。

扩展UserDetails

通过自定义 UserDetails 的实现,我们可以将其作为数据库实体与Authentication 的适配者。

数据库实体 User:

@Data
@Document(collection = "db_user")
class User{
    @Id
    private String id;
    private String username;
    private String password;
    private String[] roles;
}

适配器实体 CustomUserDetails:

@EqualsAndHashCode(callSuper = true)
@Data
public class CustomUserDetails extends User implements UserDetails {

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return AuthorityUtils.createAuthorityList(super.getRoles());
    }

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

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

    @Override
    public boolean isAccountNonExpired() {
        return false;
    }

    @Override
    public boolean isAccountNonLocked() {
        return false;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return false;
    }

    @Override
    public boolean isEnabled() {
        return false;
    }
}

这样,我们就可以通过 CustomUserDetails 在 User 与 Authentication 之间相互转换,实现数据库认证 SpringSecurity。

那么 UserDetails 对象是从哪里创建的呢?就是下面的 UserDetailsService 类。

六、UserDetailsService

UserDetailsService 只有一个特殊方法,接收一个 string 的用户名参数,并返回一个 UserDetails。

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;

通过自定义实现 UserDetailsService,返回自己的 CustomUserDetails 对象。

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if(user == null)
            throw new UsernameNotFoundException("user not found");

        final CustomUserDetails userDetails = new CustomUserDetails();
        BeanUtils.copyProperties(user,userDetails);
        // todo 一些其他附加属性等
        return userDetails;
    }
}
文章作者: koral
文章链接: http://luokaiii.github.io/2019/07/18/读书笔记/《SpringSecurity》/5.体系结构/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自