SpringSecurity--权限管理架构介绍
目录
介绍
基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。
在很多项目里,可以设置比如,有些功能只有登录了才能进行访问,但是却不完善,我们之前做的登录认证其实只是对访问系统的控制,在认证之后再根据安全规则和策略或者说是权限来进行管理
权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
认证
身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是登录。系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡。
这个时候,难道我们这个用户已经是合法用户了,那现在就可以肆无忌惮的访问吗?不同的角色,权限不同,对资源的操作也不同,所以对于不同的用户可以访问不同的资源,这个就叫做授权
授权
授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的
解决⽅案
和其他领域不同,在 Java 企业级开发中,安全管理框架⾮常少,⽬前⽐较常⻅的就是:
Shiro
Shiro 本身是⼀个⽼牌的安全管理框架,有着众多的优点,例如轻量、简单、易于 集成、可以在JavaSE环境中使⽤等。不过,在微服务时代,Shiro 就显得⼒不从 ⼼了,在微服务⾯前和扩展⽅⾯,⽆法充分展示⾃⼰的优势。
开发者⾃定义
也有很多公司选择⾃定义权限,即⾃⼰开发权限管理。但是⼀个系统的安全,不仅 仅是登录和权限控制这么简单,我们还要考虑种各样可能存在的⽹络政击以及防彻 策略,从这个⻆度来说,开发者自己实现安全管理也并⾮是⼀件容易的事情,只有 ⼤公司才有⾜够的⼈⼒物⼒去⽀持这件事情。
Spring Security
Spring Security,作为spring 家族的⼀员,在和 Spring 家族的其他成员如 Spring Boot Spring Clond等进⾏整合时,具有其他框架⽆可⽐拟的优势,同 时对 OAuth2 有着良好的⽀持,再加上Spring Cloud对 Spring Security的 不断加持(如推出 Spring Cloud Security ),让 Spring Securiy 不知不 觉中成为微服务项⽬的⾸选安全管理⽅案。
Spring Security是⼀个功能强⼤、可⾼度定制的身份验证和访问控制的框架。或者 说⽤来实现系统中权限管理的框架。
整体架构
在的架构设计中,认证和授权 是分开的,⽆论使⽤什么样的认证⽅式。都不会影响授权, 这是两个独⽴的存在,这种独⽴带来的好处之⼀,就是可以⾮常⽅便地整合⼀些外部的解决⽅案。
认证
AuthenticationManager(认证管理器)
在Spring Security中认证是由AuthenticationManager接⼝来负责的,接⼝定义为:
- 返回 Authentication 表示认证成功
- 返回 AuthenticationException 异常,表示认证失败。
注意,参数Authentication是仅包含了用户名和密码,我们需要通过这个用户名和密码与数据库中的用户名和密码进行比对,然后比对完成之后,填充完整的Authentication并且返回
但是接口是无法进行认证的,所以我们需要他的实现类。 AuthenticationManager 主要实现类为 ProviderManager(提供管理器)
在 ProviderManager 中管理了众多 AuthenticationProvider 实例。在⼀次完整的认证流程中,Spring Security 允许存在多套认证流程,也就是多个 AuthenticationProvider ,可以包含图片认证,短信验证码认证等⽤来实现多种认证⽅式,这些 AuthenticationProvider 都是由 ProviderManager 进⾏统⼀管理的。
Authentication
认证以及认证成功的信息主要是由 Authentication 的实现类进⾏保存的,其接⼝定义为:
getAuthorities 获取⽤户权限信息
getCredentials 获取⽤户凭证信息,⼀般指密码
getDetails 获取⽤户详细信息
getPrincipal 获取⽤户身份信息,⽤户名、⽤户对象等
isAuthenticated ⽤户是否认证成功
Authentication保存着我们的认证信息,那认证之后,我想在其他方法中随时去获取用户认证之后的信息,这时就离不开SecurityContextHolder
SecurityContextHolder
SecurityContextHolder ⽤来获取登录之后⽤户信息,但是获取到的信息并不代表就是Authentication。Spring Security 会将登录⽤户数据保存在 Session 中,方便我们下一次请求获取对应的信息。但是,为了使⽤⽅便,Spring Security在此基础上还做了⼀ 些改进,其中最主要的⼀个变化就是线程绑定。
我们可以认为SecurityContextHolder这个类就是一个本地的线程绑定的一个类,当⽤户登录成功后,Spring Security 会 将登录成功的⽤户信息保存到 SecurityContextHolder 中。 SecurityContextHolder 中的数据保存默认是通过ThreadLocal 来实现的,使⽤ ThreadLocal 创建的变量只能被当前线程访问,不能被其他线程访问和修改,也就是⽤户数据和请求线程绑定在⼀起。当登录请求处理完毕后,Spring Security 会将 SecurityContextHolder 中的数据拿出来保存到 Session 中,同时将 SecurityContexHolder 中的数据清空。
当我们发起请求时,会在我们这里经过认证之后,返回一个Authentication对象,这个类中保存着我们的用户信息,同时他会把这个类放在一个本地线程,ThreadLocal所绑定的叫做SecurityContextHolder这个类中
以后每当有请求到来时,如果我们携带的SessionID,能在服务器锻找到Session,同时看到Sessiom里面有Authentication这个类,就说明已经认证了。Spring Security 就会先从 Session 中取出⽤户登录数据,保存到 SecurityContextHolder 中,⽅便在该请求的后续处理过程中使⽤,同时在请求结束时将 SecurityContextHolder 中的数据拿出来保存到 Session 中,然后将 Security SecurityContextHolder 中的数据清空。
这⼀策略⾮常⽅便⽤户在 Controller、Service 层以及任何代码中获取当前登录 ⽤户数据。
授权
当完成认证后,接下来就是授权了。在 Spring Security 的授权体系中,有两个关键接口,
AccessDecisionManager
AccessDecisionManager (访问决策管理器),⽤来决定此次访问是否被允许。
那他是基于什么放行呢,基于decide方法,同时需要AccessDecisionVoter配合
AccessDecisionVoter
AccessDecisionVoter (访问决定投票器),投票器会检查⽤户是否具备应有的⻆⾊,进⽽投出赞成、反对或者弃权票。
AccesDecisionVoter 和 AccessDecisionManager 都有众多的实现类,在 AccessDecisionManager 中会挨个遍历 AccessDecisionVoter,进⽽决定是否允许⽤户访问,如果一个投票器是赞成的,那就说明这个角色是可以访问对应资源的
因⽽ AaccesDecisionVoter 和 AccessDecisionManager 两者的关系类似于 AuthenticationProvider 和 ProviderManager 的关系。
投票器用来检查⽤户是否具备应有的⻆⾊,那角色信息存在哪里呢?注意授权时的⻆⾊信息都存储在ConfigAttribute中。
ConfigAttribute
ConfigAttribute,⽤来保存授权时的⻆⾊信息
在 Spring Security 中,⽤户请求⼀个资源(通常是⼀个接⼝或者⼀个 Java ⽅法)需要的⻆⾊会被封装成⼀个 ConfigAttribute 对象,在 ConfigAttribute 中只有⼀个 getAttribute⽅法,该⽅法返回⼀个 String 字符串,就是⻆⾊的名称。
⼀般来说,⻆⾊名称都带有⼀个 ROLE_ 前缀,投票器 AccessDecisionVoter 所做的事情,其实就是⽐较⽤户所具各的⻆⾊和请求某个 资源所需的 ConfigAtuibute 之间的关系。
总结
也就是说,认证的时候会用到AuthenticationManager,认证成功之后会把信息存到Authentication中,同时为了方便获取认证后的信息,我们可以通过SecurityContextHolder来获取
而当请求访问某个资源的时候,要先经过AccessDecisionManager,然后他会将当前用户所对应的角色封装成ConfigAttribute,同时会调用AccessDecisionVoter去调用接口或者是资源上的权限与ConfigAttribute进行对比,如果返回赞成,则说明用户对该资源有访问权限