From 82a6acfe5e5e71f916282e4d753ac09c0f1019d3 Mon Sep 17 00:00:00 2001 From: asahi Date: Thu, 21 Mar 2024 19:57:14 +0800 Subject: [PATCH] =?UTF-8?q?spring=20security=E6=96=87=E6=A1=A3=E9=98=85?= =?UTF-8?q?=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring/Spring Security/Spring Security.md | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/spring/Spring Security/Spring Security.md b/spring/Spring Security/Spring Security.md index 15b4252..b79b707 100644 --- a/spring/Spring Security/Spring Security.md +++ b/spring/Spring Security/Spring Security.md @@ -42,6 +42,15 @@ - [匿名认证配置](#匿名认证配置) - [处理logout](#处理logout) - [自定义登出url](#自定义登出url) + - [Authorization](#authorization) + - [结构](#结构) + - [Invocation Handling](#invocation-handling) + - [基于委托的AuthorizationManager实现](#基于委托的authorizationmanager实现) + - [AuthorityAuthorizationManager](#authorityauthorizationmanager) + - [AuthenticatedAuthorizationManager](#authenticatedauthorizationmanager) + - [AuthorizationManagers](#authorizationmanagers) + - [自定义AuthorizationManager](#自定义authorizationmanager) + - [ROLE继承](#role继承) # Spring Security @@ -807,6 +816,107 @@ http > > logoutUrl则是触发logout操作的url。 +## Authorization +### 结构 +通过`AuthenticationManager`,`GrantedAuthority`对象集合被插入到`Authentication`对象中,该集合代表被授予给当前主体的权限。插入到Authentication中的`GrantedAuthority`集合将会后续被`AccessDecisionManager`用于判断当前主体是否拥有访问权限。 + +`GrantedAuthority`接口中只有一个方法: +```java +String getAuthority(); +``` +该方法只会被`AuthorizationManager`使用,通过该方法可以获取代表该权限的String。如果`GrantedAuthority`无法通过一个String精确的描述,那么该GrantedAuthority将被视为`复杂的`,并且该GrantedAuthority的`getAuthority`方法必须返回null。 + +如下展示了一个复杂权限的示例: + +一个`GrantedAuthority`中存储了多个客户的权限信息,每个客户可以进行的操作和可以访问资源的权限都不相同。对于多个客户的权限信息,很难勇单个String来表示,故而,在`复杂权限`的实现里,`getAuthority`方法必须返回为null。 + +如果GrantedAuthority实现的getAuthority方法返回为null,这将提示AuthorizationManager需要支持该特定`GrantedAuthority`实现类的解析,从而才能读取authority实现中的内容。 + +Spring Security中包含一个`GrantedAuthority`的实现:`SimpleGrantedAuthority`。该实现能够让任何字符串转化为对应的GrantedAuthority。并且,在Spring Security中所有的AuthenticationProvider,都使用SimpleGrantedAuthority来注入权限到Authentication对象中。 + +默认情况下,基于角色的权限管理都以`ROLE_`作为代表权限字符串的开头,故而如果当前存在Authorization Rule要求security context中拥有`USER`权限,那么Spring Security中是否包含`getAuthority`返回为`ROLE_USER`的GrantedAuthority对象。 + +如果想要自定义基于角色的权限管理的前缀,可以通过`GrantedAuthorityDefaults`来进行自定义。通过注册`GrantedAuthorityDefaults`类型的bean对象,可以进行自定义: +```java +@Bean +static GrantedAuthorityDefaults grantedAuthorityDefaults() { + return new GrantedAuthorityDefaults("MYPREFIX_"); +} +``` +#### Invocation Handling +Spring Security通过拦截器保证了对受保护资源的访问,对于method invocation和web request都能进行拦截。AuthorizationManager既会在调用发生前判断当前是否存在调权限,也会在调用后判断是否调用结果允许被返回。 + +AuthorizationManager对AccessDecisionManager和AccessDecisionVoter两者都进行了取代。 + +AuthorizationManager被spring security的基于基于method组件、基于request组件、基于message组件调用,并且AuthorizationManager负责做最后的权限控制决定。AuthorizationManager包含有如下两个方法: +```java +AuthorizationDecision check(Supplier authentication, Object secureObject); + +default AuthorizationDecision verify(Supplier authentication, Object secureObject) + throws AccessDeniedException { + // ... +} +``` +在调用`AuthorizationManager#check`方法时,会向其中传入为做出访问决定所需的所有相关信息。特别是传递了secure object,这令实际secure object调用中所有的参数都能够在check方法中被观测到。 + +例如,假定该secure object是`MethodInvocation`,通过MethodInvocation可以方便的访问调用方法和调用参数,并且在check方法中实现一系列逻辑判断当前主体是否拥有权限执行操作。 + +check方法会返回AuthorizationDecision类型的返回值,如果允许当前访问,应该返回一个positive AuthorizationDecision;如果拒绝当前访问,应该返回一个negative AuthorizationDecision;如果AuthorizationManager放弃做决定,则应该返回null。 + +`AuthorizationManager#verify`方法会调用check方法,若check方法返回negative AuthorizationDecision,则verify方法会抛出AccessDeniedException。 + +#### 基于委托的AuthorizationManager实现 +Spring Security设计了一个委托AuthorizationManager,其可以和开发者自己实现的复数个AuthorizationManager协作。 + +`RequestMatcherDelegatingAuthorizationManager`将会为当前请求匹配最适合的AuthorizationManager,并将任务委托给匹配到的AuthorizationManager。对于基于方法的权限认证,可以使用`AuthorizationManagerBeforeMethodInterceptor`和`AuthorizationManagerAfterMethodInterceptor`。 + +通过上述的委托机制,可以将多个AuthenticationManager组合起来,并通过轮询来做Authorization decision。 + +##### AuthorityAuthorizationManager +`AuthorityAuthorizationManager`是spring security提供的最通用的`AuthorizationManager`。 + +AuthorityAuthorizationManager构造方法接收一系列的权限字符串,并且在对Authentication进行校验时,会校验构造函数中接收的权限字符串集合,其中是否有一个权限字符串在`authentication.getAuthorities`中存在,如果任一权限字符串存在,那么权限校验通过,否则权限校验失败。 + +##### AuthenticatedAuthorizationManager +AuthenticatedAuthorizationManager可以用于区分anonymous、fully-authenticated、remember-me请求。这可以用于如下场景:通过remember-me登录的用户只能够执行有限的操作,但是通过full-authenticated登录的用户可以执行所有的操作。 + +##### AuthorizationManagers +`AuthorizationManagers`类中包含静态工厂方法来将多个单独的AuthorizationManager整合在一起,构成更加复杂的表达式。 + +##### 自定义AuthorizationManager +可以通过实现AuthorizationManager接口来实现任何逻辑的实现类。 + +#### ROLE继承 +通过角色继承,可以配置某些角色包含其他角色,可执行其他角色能做的所有操作。 + +`RoleHierarchyVoter`作为`RoleVoter`的拓展版本,可以配置`RoleHierarchy`: + +```java +@Bean +static RoleHierarchy roleHierarchy() { + RoleHierarchyImpl hierarchy = new RoleHierarchyImpl(); + hierarchy.setHierarchy("ROLE_ADMIN > ROLE_STAFF\n" + + "ROLE_STAFF > ROLE_USER\n" + + "ROLE_USER > ROLE_GUEST"); + return hierarchy; +} + +// and, if using method security also add +@Bean +static MethodSecurityExpressionHandler methodSecurityExpressionHandler(RoleHierarchy roleHierarchy) { + DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + expressionHandler.setRoleHierarchy(roleHierarchy); + return expressionHandler; +} +``` +在上述示例中,具有`ADMIN => STAFF => USER => GUEST`的继承关系。一个拥有`ROLE_ADMIN`角色的用户将自动拥有其他角色的权限,其中`>`符号代表包含关系。 + + + + + + +