Spring security 定义了一个过滤器链, 当认证请求到达这个链时, 该请求将会穿过这个链条用于认证和受权. 这个链上的能够定义1..N个过滤器, 过滤器的用途是获取请求中的认证信息, 根据认证方式进行路由, 把认证信息传递给对应的认证处理程序进行处理. 下面的示例图显示了Spring security中经常使用的认证过滤器.java
不一样的过滤器处理不一样的认证信息. 例如react
下图显示了多种类型的 AuthenticationToken, 基于不一样额认证方式, 过滤器会建立不一样类型的 AuthenticationTokenweb
这里咱们以最经常使用表单登陆为例子, 用户在登陆表单中输入用户名和密码, 并点击肯定, 浏览器提交POST请求到服务器, 穿过过滤器链, 被 UsernamePasswordAuthenticationFilter 识别, UsernamePasswordAuthenticationFilter 提取请求中的用户名和密码来建立 UsernamePasswordAuthenticationToken 对象.spring
组装好的 UsernamePasswordAuthenticationToken 对象被传递给 AuthenticationManagager 的 authenticate 方法进行认证决策.浏览器
public interface AuthenticationManager { Authentication authenticate(Authentication authentication)throws AuthenticationException; }
AuthenticationManager 只是一个接口, 实际的实现是 ProviderManager
ProviderManager 有一个配置好的认证提供者列表(AuthenticationProvider), ProviderManager 会把收到的 UsernamePasswordAuthenticationToken 对象传递给列表中的每个 AuthenticationProvider 进行认证.安全
AuthenticationProvider 接口的定义以下:服务器
public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException; boolean supports(Class<?> authentication); }
框架提供了一部分现有的实现类:框架
CasAuthenticationProvider
JaasAuthenticationProvider
DaoAuthenticationProvider
OpenIDAuthenticationProvider
RememberMeAuthenticationProvider
LdapAuthenticationProvider
上面咱们说了, ProviderManager 会把收到的 UsernamePasswordAuthenticationToken 对象传递给列表中的每个 AuthenticationProvider 进行认证.
那到底UsernamePasswordAuthenticationToken
会被哪个接收和处理呢?
注意观察 AuthenticationProvider 接口的 supports 方法!
部分认证提供者会使用 UserDetailsService 去获取用户信息. UserDetailsService 获取的对象是一个 UserDetails. 框架中自带一个 User 实现, 可是通常咱们须要对 UserDetails 进行定制, 内置的 User 太过简单实际项目没法知足须要.ide
下面是一个基于JPA的 UserDetailsService 实现:this
package com.example.demowebfluxsecurity.authentication; import com.example.demowebfluxsecurity.entity.Role; import com.example.demowebfluxsecurity.entity.User; import com.example.demowebfluxsecurity.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.ReactiveUserDetailsService; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; import java.util.Optional; @Service public class JpaReactiveUserDetailsService implements ReactiveUserDetailsService { private UserRepository userRepository; @Autowired public void setUserRepository(UserRepository userRepository) { this.userRepository = userRepository; } /** * @param s 用户名 * @return Mono<UserDetails> */ @Override public Mono<UserDetails> findByUsername(String s) { // 从用户Repository中获取一个User Jpa实体对象 Optional<User> optionalUser = userRepository.findByUsername(s); if (!optionalUser.isPresent()) { return Mono.empty(); } User user = optionalUser.get(); // 填充权限 List<SimpleGrantedAuthority> authorities = new ArrayList<>(); for (Role role : user.getRoles()) { authorities.add(new SimpleGrantedAuthority(role.getName())); } // 返回 UserDetails return Mono.just(new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), authorities )); } }
用户 DAO
@Repository public interface UserRepository extends JpaRepository<User, Long> { User findByEmail(String email); @Override void delete(User user); Optional<User> findByUsername(String username); }
若是认证成功(用户名,密码彻底正确), AuthenticationProvider 将会返回一个彻底有效的 Authentication 对象(UsernamePasswordAuthenticationToken). 不然抛出 AuthenticationException 异常.
彻底有效的 Authentication 对象定义以下:
authenticated
属性为 true
GrantedAuthority
列表)若是抛出异常, 将会被对应的 AuthenticationEntryPoint 处理.
可参考 UsernamePasswordAuthenticationToken 类以及抽象父类 AbstractAuthenticationToken
认证完成后, AuthenticationManager 将会返回该认证对象(UsernamePasswordAuthenticationToken)返回给过滤器
SecurityContextHolder.getContext().setAuthentication(authentication);
相关的过滤器得到一个认证对象后, 把它存储在安全上下文中(SecurityContext) 用于后续的受权判断(好比查询,修改等操做).
受权由 Authorization Filters (受权过滤器) 进行处理.