Spring Security 的 Web 应用和指纹登录实践(2)
- UID
- 1066743
|
Spring Security 的 Web 应用和指纹登录实践(2)
Spring Security 在 Web 中的设计Spring Security 的一个常见应用场景就是 Web。下面讨论 Spring Security 在 Web 中的使用方式。
Spring Security 最简登录实例Spring Security 在 Web 中的使用相对要复杂一点,会涉及到很多组件。现在给出自定义登录的伪代码,如清单 6 所示。您可以 ,查看完整的代码。
清单 6. Web 登录伪代码1
2
3
4
5
6
7
8
9
10
11
12
| @Controller
public class UserController {
@PostMapping(“/login”)
public void login(String name, String password){
matchNameAndPassword(name, password);
User user = getUser(name);
Authentication auth = new CustomAuthentication(user, password);
auth.setAuthenticated(true);
SecurityContextHolder.getContext.setAuthentication(auth);
}
}
|
观察代码会发现,如果用 Spring Security 来集成已存在的登录逻辑,真正和 Spring Security 关联的代码只有短短 3 行。验证逻辑可以不经过 AuthenticationManager,真正需要做的就是把经过验证的用户信息注入到 Authentication 中,并将 Authentication 填充到 SecurityContext 中。在实际情况中,登录逻辑的确可以这样写,尤其是已经存在登录逻辑的时候,通常会这样写。这样写虽然方便,但是不符合 Spring Security 在 Web 中的架构设计。
下面视频中会介绍已存在的项目如何与 Spring Security 进行集成,要求对已存在的登录验证逻辑不变,但可以使用 Spring Security 的优秀特性和功能。
Spring Security 在 Web 中的核心组件下面介绍在 Web 环境中 Spring Security 的核心组件。
FilterChainProxyFilterChaniProxy 是 FilterChain 代理。FilterChain 维护了一个 Filter 队列,这些 Filter 为 Spring Security 提供了强大的功能。一个很常见的问题是:Spring Security 在 Web 中的入口是哪里?答案是 Filter。Spring Security 在 Filter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验。Spring Security 选择 Filter,而没有采用上文中 Controller 的方式有以下优点。Spring Security 依赖 J2EE 标准,无需依赖特定的 MVC 框架。另一方面 Spring MVC 通过 Servlet 做请求转发,如果 Spring Security 采用 Servlet,那么 Spring Security 和 Spring MVC 的集成会存在问题。FilterChain 维护了很多 Filter,每个 Filter 都有自己的功能,因此在 Spring Security 中添加新功能时,推荐通过 Filter 的方式来实现。
ProviderManagerProviderManager 是 AuthenticationManager 的实现类。ProviderManager 并没有实现对 Authentication 的校验功能,而是采用代理模式将校验功能交给 AuthenticationProvider 去实现。这样设计是因为在 Web 环境中可能会支持多种不同的验证方式,比如用户名密码登录、短信登录、指纹登录等等,如果每种验证方式的代码都写在 ProviderManager 中,想想都是灾难。因此为每种验证方式提供对应的 AuthenticationProvider,ProviderManager 将验证任务代理给对应的 AuthenticationProvider,这是一种不错的解决方案。在 ProviderManager 中可以找到以下代码,如清单 7 所示:
清单 7. ProviderManager 代码片段1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| private List<AuthenticationProvider> providers;
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
......
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
}
}
|
ProviderManager 维护了一个 AuthenticationProvider 队列。当 Authentication 传递进来时,ProviderManager 通过 supports 函数查找支持校验的 AuthenticationProvider。如果没有找到支持的 AuthenticationProvider 将抛出 ProviderNotFoundException 异常。
AuthenticationProviderAuthenticationProvider 是在 Web 环境中真正对 Authentication 进行校验的组件。其接口签名如清单 8 所示:
清单 8. AuthenticationProvider 的接口签名1
2
3
4
5
| public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
|
其中,authenticate 函数用于校验 Authentication 对象;supports 函数用于判断 provider 是否支持校验 Authentication 对象。
当应用添加新的验证方式时,验证逻辑需要写在对应 AuthenticationProvider 中的 authenticate 函数中。验证通过返回一个重新注入的 Authentication,验证失败抛出 AuthenticationException 异常。 |
|
|
|
|
|