Spring Security概述

介绍

Spring Security是一个功能强大且高度且可定制的身份验证和访问控制框架,除了标准的身份认证和授权之外,它还支持点击劫持,CSRF,XSS,MITM(中间人)等常见攻击手段的保护,并提供密码编码,基于HTTP Basic的身份验证、基于HTTP Digest的身份验证、LDAP认证,Session管理,Remember Me认证,JWT,OAuth 2.0等功能特性。

​ Spring Security的核心思想是认证和授权,即验证用户身份并授予相应的权限。 Spring Security提供了一些基本的安全特性,如身份验证、访问控制和数据加密等。

什么是权限管理

​ 权限管理属于系统安全的范畴,用于实现对用户访问系统的可控制访问。权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限才可访问。

什么是认证

​ **认证 ,就是判断⼀个用户是否为合法用户的处理过程。**最常用的简单身份认证方式是系统通过核对用户输入的用户名和口令(密码),看其是否与系统中存储的该用户的用户名和口令⼀致,来判断用户身份是否正确。

什么是授权

​ 授权 ,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限才可访问系统的资源,对于某些资源没有权限是无法访问的。

架构设计

​ 在Spring Security的架构设计中, 认证授权 是分开的,但无论使用什么样的认证方式,都不会影响授权。

认证

​ 在Spring Security中认证是由 AuthenticationManager 来负责的。当返回Authentication 时,表示认证成功;当返回AuthenticationException异常时,表示认证失败。

Authentication接口,认证以及认证成功的信息主要是由 Authentication 的实现类进行保存的。

public interface AuthenticationManager {
    Authentication authenticate(Authentication var1) 
    			throws AuthenticationException;
}

public interface Authentication extends Principal, Serializable {
  //获取用户权限信息
  Collection<? extends GrantedAuthority> getAuthorities();
  //获取用户凭证信息,⼀般指密码  
  Object getCredentials();
  //获取用户详细信息
  Object getDetails();
  //获取用户身份信息,用户名、用户对象等
  Object getPrincipal();
  //用户是否认证成功
  boolean isAuthenticated();
  void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

​ 而AuthenticationManager主要实现类为 ProviderManager,在 ProviderManager 中管理了众多AuthenticationProvider实例。在⼀次完整的认证流程中,Spring Security允许存在多个 AuthenticationProvider ,用来实现多种认证方式,这些 AuthenticationProvider 都是由 ProviderManager 进行统⼀管理的。

SecurityContextHolder 类,SecurityContextHolder 用来获取登录之后用户信息。

执行流程

​ 1)Spring Security 会将登录用户数据保存在 Session 中。但是,为了使用方便,Spring Security在此基础上还做了一些改进,其中最主要的⼀个变化就是线程绑定。

​ 当用户登录成功后,Spring Security 会将登录成功的用户信息统一保存到SecurityContextHolder 中。SecurityContextHolder 中的数据保存默认是通过ThreadLocal 来实现的,使用ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是用户数据和请求线程绑定在⼀起。

所以当登录请求处理完毕后,Spring Security 会将 SecurityContextHolder 中的数据拿出来保存到 Session 中,同时将 SecurityContexHolder 中的数据清空。

授权

在 Spring Security 的授权体系中,有两个关键接口:AccessDecisionManagerAccessDecisionVoter。AccessDecisionManager (访问决策管理器),用来决定此次访问是否被允许。AccessDecisionVoter (访问决定投票器),投票器会检查⽤户是否具备应有的角色,进而投出赞成、反对或者弃权票。

而AccessDecisionManager决定此次访问是否被允许,会挨个遍历 AccessDecisionVoter,进而决定是否允许用户访问。

​ 在 Spring Security 中,用户请求⼀个资源需要的角色会被封装成一个 ConfigAttribute 对象,在 ConfigAttribute 中只有⼀个 getAttribute方法,该方法返回⼀个 String 字符串,就是角色的名称。⼀般来说,角色名称都带有⼀个 ROLE_ 前缀,投票器AccessDecisionVoter 所做的事情,其实就是比较用户所具各的角色和请求某个资源所需的 ConfigAtuibute 之间的关系。

使用

​ 以前 Spring Security 的配置,相当繁琐。依托于 Spring Boot ,现在 Spring Security 的配置项简化了很多。甚至最基础的使用只需要引入 jar 包就可以了。

Spring Boot 的 Security 自动配置:

  • Spring Security 的默认配置,在 Servlet 过滤器中创建名为 springSecurityFilterChain 的 bean。这个 bean 负责应用程序内的所有安全验证(保护URL,验证用户名和密码,重定向到登录表单等)。
  • 创建一个 UserDetailsService bean,其中包含用户名和一个随机生成的密码,密码打印到控制台。
  • 针对每个请求,向 servlet 容器过滤器 注册 springSecurityFilterChain 。

而对于自定义逻辑控制认证逻辑,只需要实现UserDetailsService接口。

PasswordEncoder 是SpringSecurity 的密码解析器,用户密码校验、加密 。 自定义登录逻辑时要求必须给容器注入PaswordEncoder的bean对象。而对于业务场景不同,需要特定的加密解密,可实现该接口。

@Component
public class UserSecurity implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

        User user = userService.login(userName);
        System.out.println(user);
        if (null==user){
            throw new UsernameNotFoundException("用户名错误");
        }
        org.springframework.security.core.userdetails.User result =
                new org.springframework.security.core.userdetails.User(
                        userName,user.getPassword(), AuthorityUtils.createAuthorityList()
                );
        return result;
    }
}

RBAC 权限设计思想

**RBACRole-Based Access Control 的首字母,即基于角色的权限访问控制。目前这是用到最多的权限系统设计思想。最简单的 RBAC 系统,是「用户-角色-权限」的授权模型,又被称为RBAC0。在这种模型之中,用户与角色、角色与权限,一般是多对多的关系。** 当然,在我们业务开发过程中,还是需要根据实际进行扩展,这仅仅作为一个指导思想。

原理分析

SpringSecurity本质是用一个Filter链实现认证的功能,利用过滤器过滤需要认证的Request,然后进行认证。然而SpringSecurity并不是直接把所有Filter直接注册到服务容器(比如Tomcat)上。而是使用了一个DelegatingFilterProxy 的Filter代理类。

​ 由于SpringSecurity支持多种模式认证,所以有可能不止一个FilterChain。FilterChainProxy可以根据请求路径,调用匹配到的SecurityFilterChain。

使用代理的好处是:

一方面可以让Security的Filter交给spring管理,进而spring的一些功能(Filter注册肯定要在Listener调用前完成,而spring是通过ContextLoaderListener 加载Bean的,使用代理的话可以延迟加载,才不会在spring ioc没加载前使用它)。
​ 另外一方面,可以将Spring的Filter和其他Filter隔离开来。
由于DelegatingFilterProxy 是一个普通Filter,如果通过延迟加载所有 Filter 那也太麻烦了,如果让一个注册到Spring中的代理去操作具体的Filter,而DelegatingFilterProxy 就只需要延迟加载这个代理(直接写死在配置文件中,通过读取配置security的web配置文件,然后从server中拿)。而这个被用来代理处理具体和spring交互的就是 FilterChainProxy 。

FilterChainProxy 杂负责拿到SecurityFilterChain,并负责调用。

在实际应用场景中,我们常常将这个庞大的Filter拆分成多个小Filter,并将它们链接在一起。每个Filter都只负责特定领域的功能,比如CsrfFilterAuthenticationFilterAuthorizationFilter等。

其他

1、CSRF

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack” 或者Session Riding。通过伪造用户请求访问受信任站点的非法请求访问。

​ 跨域:只要网络协议,ip地址,端口中任何一个不相同就是跨域请求。

​ 客户端与服务进行交互时,由于http协议本身是无状态协议,所以引入了cookie进行记录客户端身份。在cookie中会存放session id用来识别客户端身份的。在跨域的情况下,session id可能被第三方恶意劫持,通过这个session id向服务端发起请求时,服务端会认为这个请求是合法的,可能发生很多意想不到的事情。

通俗解释:

​ CSRF就是别的网站非法获取我们网站Cookie值,我们项目服务器是无法区分到底是不是我们的客户端,只有请求中有Cookie,认为是自己的客户端,所以这个时候就出现了CSRF。

2、认证和鉴权失败抛出的异常是如何处理的?

​ 当发生认证或鉴权失败时,Spring Security有专门的Security Filter ExceptionTranslationFilter来捕获并处理这些异常。如果是认证异常错误AuthenticationException及其子类,会触发AuthenticationEntryPoint#commence方法,而如果是鉴权错误AccessDeniedException及其子类,则会触发AccessDeniedHandler#handle方法。

3、一个请求被Security拒绝了,应该如何Debug排查?

​ 如果遇到身份认证错误,建议直接Debug相关Filter的doFilter方法,比如Form表单登录的Filter就是UsernamePasswordAuthenticationFilter

​ 而如果是鉴权错误,可以从AuthorizationFilter开始Debug。

​ 但需要注意的是,出于安全考虑,Security相关的错误通常不会提供明确的错误信息,甚至不会显示错误信息,而是直接跳转到登录页面,比如CsrfFilter可能会导致这种情况。在这种情况下,可以从第一个Filter开始Debug,启动日志搜索Will secure any request with,就可以找到所有Security Filter列表。或者直接从入口FilterChainProxy#doFilter开始Debug。

你可能感兴趣的:(中间件,java)