diff --git a/spring/Spring Security/Spring Security.md b/spring/Spring Security/Spring Security.md index 9ccc5f4..15b4252 100644 --- a/spring/Spring Security/Spring Security.md +++ b/spring/Spring Security/Spring Security.md @@ -38,6 +38,10 @@ - [将token持久化到数据库中](#将token持久化到数据库中) - [RememberMe的接口及其实现](#rememberme的接口及其实现) - [TokenBasedRememberMeService](#tokenbasedremembermeservice) + - [匿名身份认证](#匿名身份认证) + - [匿名认证配置](#匿名认证配置) + - [处理logout](#处理logout) + - [自定义登出url](#自定义登出url) # Spring Security @@ -732,5 +736,83 @@ RememberMeAuthenticationProvider rememberMeAuthenticationProvider() { return rememberMeAuthenticationProvider; } ``` +## 匿名身份认证 +Spring Security中,采用了`deny-by-default`策略,用以适配如下场景:除少数资源外(如homepage、login页面等),访问其他所有的资源都需要进行身份认证。 + +Spring Security提供了匿名身份认证,对于“匿名身份认证”的用户,其实际和未经认证的用户没有任何区别。匿名认证只是为配置访问控制属性提供了更加便捷的方式,在匿名认证时,即使当前`SecurityContextHolder`中存在`anonymous authentication`对象,在调用一些servlet api例如`getCallerPrincipal`方法时,方法仍然会返回null。 + +在使用anonymous authentication时,Spring Security的SecurityContextHolder一定含有Authentication对象,即使用户未经认证,Authentication对象也不可能为空。 + +### 匿名认证配置 +匿名认证在Spring Security中是默认被提供的,可以对其进行自定义或者关闭。 + +为了提供匿名认证特性,需要存在如下三个类:`AnonymousAuthenticationToken`、`AnonymousAuthenticationProvider`、`AnonymousAuthenticationFilter`。 +- `AnonymousAuthenticationToken`:该类是`Authentication`的实现类,并且存储了该匿名实体所拥有的权限`GrantedAuthority` +- `AnonymousAuthenticationProvider`:该类被整合到`ProviderManager`的provider链中,故而`AnonymousAuthenticationToken`可以被支持进行处理 +- `AnonymousAuthenticationFilter`:该filter被嵌入到常规的认证机制之后,如果在执行到该filter时,当前SecurityContextHolder中不存在Authentication,那么其会自动将`AnonymousAuthenticationToken`加入到SecurityContextHolder中。 + +定义filter和provider的方式如下所示,filter和provider共享相同的key,故而filter创建的token可以被provider接收: +```java + + + + + + + + +``` + +`userAttribute`属性值,其格式为`usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]`。 + + +## 处理logout +如果当前项目的类路径中包含`spring-boot-starter-security`依赖,那么spring security会自动加入logout支持,针对`GET`和`POST`的`/logout`请求都会响应。 + +当通过get方式请求/logout时,spring security将展示一个登出页面。但如果csrf保护被关闭,那么不会展示登出确认页面,而是直接关闭。 + +当请求/logout时,将通过一系列logoutHandler来实现如下逻辑: +1. 将http session标记为失效(SecurityContextLogoutHandler) +2. 清空SecurityContextHolderStrategy (SecurityContextLogoutHandler) +3. 清空SecurityContextRepository(SecurityContextLogoutHandler) +4. 清空所有rememberMe authentication(TokenRememberMeServices / PersistentTokenRememberMeServices) +5. 清空任何csrf token (LogoutSuccessEventPublishingLogoutHandler) +6. 发送LogoutSuccessEvent(LogoutSuccessEventPublishingLogoutHandler) + +### 自定义登出url +在filter chain中,logoutFilter位于AuthorizationFilter之前,故而没有必要显式的对/logout url执行permit操作(因为尚未执行到AuthorizationFilter之前,就会通过logoutFilter执行登出操作)。 + +但是,对于自定义的登出端口(默认情况下,logooutFilter只对/logout的请求进行处理),若url不为/logout,那么需要调用`permitAll`来让自定义登出端口可访问。 + +如果只是要修改spring security登出的url,可以通过如下方式进行修改,且不需要其他任何修改,其单单只是改变了logoutFilter匹配的url: +```java +http + .logout((logout) -> logout.logoutUrl("/my/logout/uri")) +``` + +但是,如果自定义了logout success endpoint(通过spring mvc建立的controller endpoint),那么需要为自定义的logout endpoint执行permit操作来允许用户访问。因为只有当spring security中的操作都完成后,才会指定自定义的endpoint操作。 + +可以通过如下方式为自定义的登出endpoint进行授权: +```java +http + .authorizeHttpRequests((authorize) -> authorize + .requestMatchers("/my/success/endpoint").permitAll() + // ... + ) + .logout((logout) -> logout.logoutSuccessUrl("/my/success/endpoint")) +``` +> logoutSuccessUrl为logout操作执行成功后,重定向到的url,该值默认为`/login?logout`。 +> +> logoutUrl则是触发logout操作的url。 + + + + + + + +