Spring-Security-文档笔记之认证机制

1. 用户名密码认证

用户名密码读取方式有三:

  • 表单
  • Basic认证
  • Digest认证

存储机制:

  • 内存存储
  • JDBC存储
  • 自定义存储 UserDetailsService
  • LDAP存储
1.1 表单登录

用户是如何被重定向到登录表单的:


image.png
  1. 用户向/private发起未认证请求.
  2. FilterSecurityInterceptor通过抛出AccessDeniedException表示拒绝这个请求.
  3. 因为用户未认证, ExceptionTranslationFilter开始认证过程并通过AuthenticationEntryPoint发送一个重定向到登录页面的响应.
  4. 浏览器重定向到登录页面.
  5. 显示登录页面.

用户提交用户名密码后的处理过程:


image.png
  1. 当用户提交用户名密码后, UserPasswordAuthenticationFilter创建一个UsernamePasswordAuthenticationToken(从请求中解析用户名密码)
  2. token被传递给AuthenticationManager进行认证.
  3. 后续过程同AbstractAuthenticationProcessingFilter(UserPasswordAuthenticationFilter继承自AbstractAuthenticationProcessingFilter)

Spring Security 的表单登录功能默认开启, 但是如果提供了任何基于servlet的配置, 则需要显式配置:

// java
protected void configure(HttpSecurity http) {
    http
        // ...
        .formLogin(withDefaults());
}

// xml

    


自定义登录页面

// java
protected void configure(HttpSecurity http) throws Exception {
    http
        // ...
        .formLogin(form -> form
            .loginPage("/login")
            .permitAll()
        );
}

// xml

    
    

登录表单

  • 表单通过/login POST请求提交登录数据
  • 表单需要包含一个CSRF token.
  • 用户名参数名为 username
  • 密码参数为 password

以上都可以自行配置, 如自定义登录页面, 则需要提供一个get方式的login请求, 以跳转到登录页面.
java配置见FormLoginConfigurer. XML配置见

1.2 Basic验证

Basic验证使用的是BasicAuthenticationEntryPoint.

配置:

// java
protected void configure(HttpSecurity http) {
    http
        // ...
        .httpBasic(withDefaults());
}

// xml

    

1.3 UserDetailsService

UserDetailsService通过使用DaoAuthenticationProvider来获取用户名,密码及其他扩展信息.

1.4 PasswordEncoder

Spring Security加密支持. 常用实现类为BCryptPasswordEncoder.

1.5 DaoAuthenticationProvider

AuthenticationProvider的实现类, 通过UserDetailsServicePasswordEncoder支持验证用户名和密码.

image.png
  1. 读取用户名和密码到UsernamePasswordAuthenticationToken中, 并将其传递给ProviderManager.
  2. ProviderManager选择调用DaoAuthenticationProvider.
  3. DaoAuthenticationProvider 通过UserDetailsService获取UserDetails.
  4. 调用PasswordEncoder验证密码.
  5. 如果验证成功, 返回UsernamePasswordAuthenticationToken(其principal为UserDetails对象). 并将其存储到SecurityContextHolder中.

2. Session Management

与Session相关的功能由SessionManagementFilterSessionAuthenticationStrategy实现. 功能包括防止Session固定会话攻击, 检测session超时和限制session并发数.

2.1 检测超时

session失效后可以重定向到session无效页面.


  

如果使用以上配置检测session超时, 如果用户退出后又不关闭浏览器重新登录,可能会报错. 这是因为cookie没有被清除,即使用户已经注销,会话cookie也会被重新提交。因此需要在logout时显式地删除JSESSIONID cookie.


    

**注意: **如果在代理后面运行应用程序,那么还可以通过配置代理服务器来删除会话cookie。

2.2 session并发控制

限制用户的登录行为.
首先: 需要在web.xml中添加session事件监听器.


  
    org.springframework.security.web.session.HttpSessionEventPublisher
  

然后: 添加并发数量限制

// 当error-if-maximum-exceeded为false时, 第二次登录将会剔掉第一次登录. 
// 如果为true, 第二次登录将会被拒绝. 如果是表单登录,则会进入认证失败页面, 如果是rememberme, 则会返回401. 

  
    
  

2.3 Session固定保护

session固定会话攻击是利用服务器的session不变机制, 攻击者欺骗用户登录(此时登录所携带的sessionId为攻击者设置的sessionId), 用户登录后, 攻击者设置的sessionId对应的会话合法了, 然后就可以利用这个sessionId冒充用户以达到目的.
解决办法就是用户登录后就修改session信息.
Spring Security通过session-fixation-protection属性控制session策略.

  • none: 不做任何改变.
  • newSession: 创建一个全新的session, 不会复制session数据, 但与spring security相关的属性会被复制到新session中.
  • migrateSession: 创建一个新的session, 然后将旧session的数据复制到新的session中. servlet3.0及之前的默认策略.
  • changeSessionId: 修改sessionId. servlet3.1及以后版本的默认策略, 这里使用的是servlet的固定会话攻击防护机制(HttpServletRequest#changeSessionId)

当发生session固定攻击时, spring 容器会发布一个SessionFixationProtectionEvent. 如果使用changeSessionId机制, HttpSessionIdListener也会收到通知.

2.4 SessionManagementFilter

工作流程:

  1. 根据当前SecurityContextHolder的内容检查SecurityContextRepository的内容,以确定用户在当前请求期间是否已通过身份验证.
  2. 如果包含SecurityContext, 则不做任何事情.如果不包含, 但是当前的 SecurityContext中包含一个匿名的Authentication对象, 它则假设用户已被前面的filter进行验证过, 它将调用SessionAuthenticationStrategy.
  3. 如果当前用户没有经过身份验证,筛选器将检查是否请求了无效的会话ID(例如由于超时),如果设置了InvalidSessionStrategy,则将调用配置的InvalidSessionStrategy。最常见的行为就是重定向到一个固定的URL,这封装在标准实现SimpleRedirectInvalidSessionStrategy中。
2.5 SessionAuthenticationStrategy

它被SessionManagementFilterAbstractAuthenticationProcessingFilter使用. 因此,如果使用定制的表单登录类,需要将它注入到这两个类中。







    
    ...



2.6 并发控制

除了前面的配置, 还需要配置ConcurrentSessionFilterFilterChainProxy. 它有两个参数: SessionRegistry, SessionInformationExpiredStrategy.
























    
    
        
        
        
    
    
    
    
        
    
    




2.7. 类图
image.png

3. Remeber-Me 认证

网站可以记住用户身份, 当用户再次访问网站时可自动登录. 这是通过cookie机制来实现的. Spring Security提供了两种实现: 一是基于cookie的实现, 二是持久存储的实现(数据库). 这两种实现都需要UserDetailsService.

3.1 基于hash的token方式

在用户身份认证成功之后会发送一个cookie到浏览器. 其格式如下:

base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))

username:          用户名
password:          密码
expirationTime:    token过期时间, 单位为毫秒
key:               防止remember-me令牌被修改的私钥

启用remeber-me


    

3.2 持久化token

使用这种方式需要提供数据源.




数据库表建表SQL:

create table persistent_logins (username varchar(64) not null,
                                series varchar(64) primary key,
                                token varchar(64) not null,
                                last_used timestamp not null)
3.3 RemeberMe接口及实现

UsernamePasswordAuthenticationFilter具有rememberme功能, 它是通过AbstractAuthenticationProcessingFilter中的钩子实现, 这个钩子调用RememberMeServices.

// RememberMeServices
Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);

void loginFail(HttpServletRequest request, HttpServletResponse response);

void loginSuccess(HttpServletRequest request, HttpServletResponse response,
    Authentication successfulAuthentication);

UsernamePasswordAuthenticationFilter只会调用loginFailloginSuccess方法. autoLogin是在RememberMeAuthenticationFilter中调用.

PersistentTokenBasedRememberMeServices
PersistentTokenBasedRememberMeServices还需要一个UserDetailsService, 以获取用户信息进行验证并生成RemeberMeAuthenticationToken. 另外它还依赖PersistentTokenRepository以用于存储token. 它默认有两种实现:

  • InMemoryTokenRepositoryImpl
  • JdbcTokenRepositoryImpl
3.4 RememberMeAuthenticationFilter类图
image.png

4. 匿名认证

4.1 介绍

“匿名身份验证”的用户和未经身份验证的用户之间并没有真正的概念上的区别。匿名身份验证的好处是,所有URI模式都可以应用安全性, 还可保证SecutiryContextHolder总是包含一个Authentication对象而不是null.

4.2 配置

  

三个类提供了身份认证功能:

  • AnonymousAuthenticationToken
    用于存储为匿名用户授权的权限(GrantedAuthority)
  • AnonymousAuthenticationProvider
  • AnonymousAuthenticationFilter








key在provider和filter之间共享,

4.3 AuthenticationTrustResolver
4.4 spring mvc中获取匿名Authentication对象.

使用@CurrentSecurityContext.

@GetMapping("/")
public String method(@CurrentSecurityContext SecurityContext context) {
    return context.getAuthentication().getName();
}

你可能感兴趣的:(Spring-Security-文档笔记之认证机制)