首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Spring Security 的 Web 应用和指纹登录实践(3)

Spring Security 的 Web 应用和指纹登录实践(3)

Spring Security 在 Web 中的认证示例下面的视频中会介绍采用 Spring Security 提供的 UsernamePasswordAuthenticationFilter 实现登录验证。


下面以用户名密码登录为例来梳理 Spring Security 在 Web 中的认证流程。上文提到 Spring Security 是以                                Filter 来作为校验的入口点。在用户名密码登录中对应的 Filter 是                                UsernamePasswordAuthenticationFilter。attemptAuthentication                                函数会执行调用校验的逻辑。在 attemptAuthentication 函数中,可以找到以下代码,如清单 9 所示:
清单 9.                                        attemptAuthentication                                函数代码片段
1
2
3
4
5
6
7
8
public Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse
response) throws AuthenticationException {
       ......
       UsernamePasswordAuthenticationToken authRequest = new
UsernamePasswordAuthenticationToken(username, password);
       setDetails(request, authRequest);
       return this.getAuthenticationManager().authenticate(authRequest);
}




attemptAuthentication 函数会调用 AuthenticationManager 执行校验逻辑,并获取到重新注入后的                                Authentication。在 UsernamePasswordAuthenticationFilter 父类                                AbstractAuthenticationProcessingFilter 的 successfulAuthentication                                函数中发现以下代码,如清单 10 所示:
清单 10.                                        successAuthentication                                函数
1
2
3
4
5
6
protected void successfulAuthentication(HttpServletRequest request,
                      HttpServletResponse response, FilterChain chain, Authentication
authResult)throws IOException, ServletException {
       ......         SecurityContextHolder.getContext().setAuthentication(authResult);
       ......
}




successfulAuthentication 函数会把重新注入的 Authentication 填充到 SecurityContext                                中,完成验证。
在 Web 中,AuthenticationManager 的实现类 ProviderManager 并没有实现校验逻辑,而是代理给                                AuthenticationProvider, 在用户名密码登录中就是                                DaoAuthenticationProvider。DaoAuthenticationProvider 主要完成 3 个功能:获取                                UserDetails、校验密码、重新注入 Authentication。在 authenticate 函数中发现以下代码,如清单 11                                所示:
清单 11. DaoAuthenticationProvider.authenticate                                        函数签名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public Authentication authenticate(Authentication authentication)
                      throws AuthenticationException {
       ......
       // 获取 UserDetails
       UserDetails user = this.userCache.getUserFromCache(username);
       if (user == null) {
              cacheWasUsed = false;
              try {
                      user = retrieveUser(username,                 
       (UsernamePasswordAuthenticationToken) authentication);
              }
              ......
       }
       ......
       try {
              ......
              //校验密码
              additionalAuthenticationChecks(
                      user,
              (UsernamePasswordAuthenticationToken) authentication
              );
       }
       ......
       // 从新注入 Authentication
       return createSuccessAuthentication(
                      principalToReturn,
                      authentication,
                      user
               );
}




首先从 userCache 缓存中查找 UserDetails, 如果缓存中没有获取到,调用 retrieveUser 函数获取                                UserDetails。retrieveUser 函数签名如清单 12 所示:
清单 12. retrieveUser                                        函数签名
1
2
3
4
5
6
7
8
9
10
protected final UserDetails retrieveUser(String username,
       UsernamePasswordAuthenticationToken authentication)
                      throws AuthenticationException {
       UserDetails loadedUser;
       try {
               loadedUser = this.getUserDetailsService().loadUserByUsername(username);
       }
       ......
       return loadedUser;
}




retrieveUser 函数调用 UserDetailsService 获取 UserDetails 对象。UserDetailsService                                接口签名如清单 13 所示:
清单 13.                                        UserDetailsService                                接口签名
1
2
3
public interface UserDetailsService {
       UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}




UserDetailsService 非常简单,只有一个 loadUserByUserName 函数,函数参数虽然名为                                username,但只要是用户的唯一标识符即可。下面是基于数据库存储的简单示例, 如清单 14 所示:
清单 14.                                        CustomUserDetailsService                                类签名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CustomUserDetailsService implements UserDetailsService {
     
    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
        User user = userDao.findByName(username);
        if(Objects.isNull(user)) {
           throw new UsernameNotFoundException();
        }
        UserDetails details = new CustomUserDetails(user);
        return details;
    }
}




调用 UserDao 获取 User 对象,将 User 对象包装成 UserDetails 对象。如果没有找到 User 对象,需要抛出                                        UsernameNotFoundException 异常。
DaoAuthenticationProvider 密码校验调用 additionalAuthenticationChecks 函数,具体通过                                PasswordEncoder 比对用户输入的密码和存储在应用中的密码是否相等,如果不相等,抛出                                        BadCredentialsException 异常。
DaoAuthenticationProvider 对 Authentication 对象的重新注入通过调用                                createSuccessAuthentication 函数, 如清单 15 所示:
清单                                        15. createSuccessAuthentication                                函数签名
1
2
3
4
5
6
7
8
9
10
11
protected Authentication createSuccessAuthentication(Object principal,
                      Authentication authentication, UserDetails user) {
        UsernamePasswordAuthenticationToken result = new        
        UsernamePasswordAuthenticationToken(
               principal,
               authentication.getCredentials(),         
               authoritiesMapper.mapAuthorities(user.getAuthorities())
        );
        result.setDetails(authentication.getDetails());
        return result;
}




以上就是 Spring Security 在 Web 环境中对于用户名密码校验的整个流程,简言之:
  • UsernamePasswordAuthenticationFilter 接受用户名密码登录请求,将                                        Authentication 传递给 ProviderManager 进行校验。
  • ProviderManager 将校验任务代理给 DaoAuthenticationProvider。
  • DaoAuthenticationProvider 对 Authentication                                        的用户名和密码进行校验,校验通过后返回重新注入的 Authentication 对象。
  • UsernamePasswordAuthenticationFilter 将重新注入的 Authentication 对象填充到                                        SecurityContext 中。
返回列表