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

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

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

前言Java 开发人员在解决 Web 应用安全相关的问题时,通常会采用两个非常流行的安全框架,Shiro 和 Spring                                Security。Shiro 配置简单,上手快,满足一般应用的安全需求,但是功能相对单一。Spring Security 安全粒度细,与                                Spring Framework 无缝集成,满足绝大多数企业级应用的安全需求,但是配置复杂,学习曲线陡峭。
Spring Security 相对 Shiro 功能强大,并且 Spring Framework,Spring Boot,Spring                                Cloud 对 Spring Security 的支持更加友好 (毕竟是 "亲儿子")。本文将介绍 Spring Security                                的架构设计、核心组件,在 Web 应用中的开发方式,最后以一个指纹登录的实例收尾。
Spring                                Security 核心设计Spring Security                                有五个核心组件:SecurityContext、SecurityContextHolder、Authentication、Userdetails                                和 AuthenticationManager。下面分别介绍一下各个组件。
SecurityContextSecurityContext 即安全上下文,关联当前用户的安全信息。用户通过 Spring Security                                的校验之后,SecurityContext 会存储验证信息,下文提到的 Authentication                                对象包含当前用户的身份信息。SecurityContext 的接口签名如清单 1 所示:
清单 1.                                        SecurityContext                                的接口签名
1
2
3
4
public interface SecurityContext extends Serializable {
       Authentication getAuthentication();
       void setAuthentication(Authentication authentication);
}




SecurityContext 存储在 SecurityContextHolder 中。
SecurityContextHolderSecurityContextHolder 存储 SecurityContext 对象。SecurityContextHolder                                是一个存储代理,有三种存储模式分别是:
  • MODE_THREADLOCAL:SecurityContext 存储在线程中。
  • MODE_INHERITABLETHREADLOCAL:SecurityContext                                        存储在线程中,但子线程可以获取到父线程中的 SecurityContext。
  • MODE_GLOBAL:SecurityContext 在所有线程中都相同。
SecurityContextHolder 默认使用 MODE_THREADLOCAL 模式,SecurityContext                                存储在当前线程中。调用 SecurityContextHolder 时不需要显示的参数传递,在当前线程中可以直接获取到                                SecurityContextHolder 对象。但是对于很多 C                                端的应用(音乐播放器,游戏等等),用户登录完毕,在软件的整个生命周期中只有当前登录用户,面对这种情况                                SecurityContextHolder 更适合采用 MODE_GLOBAL 模式,SecurityContext                                相当于存储在应用的进程中,SecurityContext 在所有线程中都相同。
AuthenticationAuthentication 即验证,表明当前用户是谁。什么是验证,比如一组用户名和密码就是验证,当然错误的用户名和密码也是验证,只不过                                Spring Security 会校验失败。Authentication 接口签名如清单 2 所示:
清单 2.                                        Authentication                                的接口签名
1
2
3
4
5
6
7
8
public interface Authentication extends Principal, Serializable {
       Collection<? extends GrantedAuthority> getAuthorities();
       Object getCredentials();
       Object getDetails();
       Object getPrincipal();
       boolean isAuthenticated();
       void setAuthenticated(boolean isAuthenticated);
}




Authentication 是一个接口,实现类都会定义                                authorities,credentials,details,principal,authenticated                                等字段,具体含义如下:
  • getAuthorities: 获取用户权限,一般情况下获取到的是用户的角色信息。
  • getCredentials: 获取证明用户认证的信息,通常情况下获取到的是密码等信息。
  • getDetails: 获取用户的额外信息,比如 IP 地址、经纬度等。
  • getPrincipal:                                        获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserDetails                                        (暂时理解为,当前应用用户对象的扩展)。
  • isAuthenticated: 获取当前 Authentication 是否已认证。
  • setAuthenticated: 设置当前 Authentication 是否已认证。
在验证前,principal 填充的是用户名,credentials 填充的是密码,detail 填充的是用户的 IP                                或者经纬度之类的信息。通过验证后,Spring Security 对 Authentication 重新注入,principal                                填充用户信息(包含用户名、年龄等), authorities 会填充用户的角色信息,authenticated 会被设置为                                true。重新注入的 Authentication 会被填充到 SecurityContext 中。
UserDetailsUserDetails 提供 Spring Security 需要的用户核心信息。UserDetails 的接口签名如清单 3 所示:
清单 3. UserDetails                                        的接口签名
1
2
3
4
5
6
7
8
9
public interface UserDetails extends Serializable {
       Collection<? extends GrantedAuthority> getAuthorities();
       String getPassword();
       String getUsername();
       boolean isAccountNonExpired();
       boolean isAccountNonLocked();
       boolean isCredentialsNonExpired();
       boolean isEnabled();
}




UserDetails 用 isAccountNonExpired,                                        isAccountNonLocked,isCredentialsNonExpired,isEnabled                                表示用户的状态(与下文中提到的                                        DisabledException,LockedException,BadCredentialsException                                相对应),具体含义如下:
  • getAuthorites:获取用户权限,本质上是用户的角色信息。
  • getPassword: 获取密码。
  • getUserName: 获取用户名。
  • isAccountNonExpired: 账户是否过期。
  • isAccountNonLocked: 账户是否被锁定。
  • isCredentialsNonExpired: 密码是否过期。
  • isEnabled: 账户是否可用。
UserDetails 也是一个接口,实现类都会继承当前应用的用户信息类,并实现 UserDetails 的接口。假设应用的用户信息类是                                User,自定义的 CustomUserdetails 继承 User 类并实现 UserDetails 接口。
AuthenticationManagerAuthenticationManager 负责校验 Authentication 对象。在 AuthenticationManager 的                                authenticate 函数中,开发人员实现对 Authentication 的校验逻辑。如果 authenticate                                函数校验通过,正常返回一个重新注入的 Authentication 对象;校验失败,则抛出                                AuthenticationException 异常。authenticate 函数签名如清单 4 所示:
清单 4. authenticate                                        函数签名
1
Authentication authenticate(Authentication authentication)throws AuthenticationException;




AuthenticationManager 可以将异常抛出的更加明确:
  • 当用户不可用时抛出 DisabledException。
  • 当用户被锁定时抛出 LockedException。
  • 当用户密码错误时抛出 BadCredentialsException。
重新注入的 Authentication 会包含当前用户的详细信息,并且被填充到 SecurityContext 中,这样 Spring                                Security 的验证流程就完成了,Spring Security 可以识别到 "你是谁"。
基本校验流程示例下面采用 Spring Security 的核心组件写一个最基本的用户名密码校验示例,如清单 5 所示:
清单 5. Spring                                        Security                                核心组件伪代码
1
2
3
4
5
6
7
8
AuthenticationManager amanager = new CustomAuthenticationManager();
Authentication namePwd = new CustomAuthentication(“name”, “password”);
try {
       Authentication result = amanager.authenticate(namePwd);
       SecurityContextHolder.getContext.setAuthentication(result);
} catch(AuthenticationException e) {
       // TODO 验证失败
}




Spring Security 的核心组件易于理解,其基本校验流程是: 验证信息传递过来,验证通过,将验证信息存储到                                SecurityContext 中;验证失败,做出相应的处理。
返回列表