Java安全框架(⼀)SpringSecurity
⽂章主要分三部分
1、Spring Security的架构及核⼼组件:(1)认证;(2)权限拦截;(3)数据库管理;(4)权限缓存;(5)⾃定义决策;
2、环境搭建与使⽤,使⽤当前热门的Spring Boot来搭建环境,结合项⽬中实际的例⼦来做⼏个Case;
3、Spring Security的优缺点总结,结合第⼆部分中⼏个Case的实现来总结Spring Security的优点和缺点。
1、Spring Security介绍
整体介绍,Spring Security为基于J2EE开发的企业应⽤软件提供了全⾯的安全服务,特别是使⽤Spring开发的企业软件项⽬,如果你熟悉Spring,尤其是Spring的依赖注⼊原理,这将帮助你更快掌握Spring Security,⽬前使⽤Spring Security有很多原因,通常因为在J2EE的Servlet规范和EJB规范中不到典型应⽤场景的解决⽅案,提到这些规范,特别要指出的是它们不能在WAR或EAR级别进⾏移植,这样如果你需要更换服务器环境,就要在新的⽬标环境中进⾏⼤量的⼯作,对你的应⽤进⾏重新配置安全,使⽤Spring Security就解决了这些问题,也为你提供了很多很有⽤的可定制的安全特性。
Spring Security包含三个主要的组件:SecurityContext、AuthenticationManager、AccessDecisionManager.
图1-1 Spring Security主要组件
1.1 认证
Spring Security提供了很多过滤器,它们拦截Servlet请求,并将这些请求转交给认证处理过滤器和访问决策过滤器进⾏处理,并强制安全性认证⽤户⾝份和⽤户权限以达到保护WEB资源的⽬的,Spring Security安全机制包括两个主要的操作,认证和验证,验证也可以称为权限控制,这是Spring Security两个主要的⽅向,认证是为⽤户建⽴⼀个他所声明的主体的过程,这个主体⼀般是指⽤户设备或可以在系统中执⾏⾏动的其他系统,验证指⽤户能否在应⽤中执⾏某个操作,在到达授权判断之前⾝份的主体已
经由⾝份认证过程建⽴了。下⾯列出⼏种常⽤认证模式,这⾥不对它们作详细介绍,需要详细了解的⽼铁们可以⾃⾏查查对应的资料。spring framework是什么框架的
1. Basic:HTTP1.0提出,⼀种基于challenge/response的认证模式,针对特定的realm需要提供⽤户名和密码认证后才可访问,其中密码使⽤明⽂传
输。缺点:①⽆状态导致每次通信都要带上认证信息,即使是已经认证过的资源;②传输安全性不⾜,认证信息⽤Base64编码,基本就是明⽂传输,很容易对报⽂截取并盗⽤认证信息。
2. Digest:HTTP1.1提出,它主要是为了解决Basic模式安全问题,⽤于替代原来的Basic认证模式,Digest认证也是采⽤challenge/response认证模
式,基本的认证流程⽐较类似。Digest模式避免了密码在⽹络上明⽂传输,提⾼了安全性,但它仍然存在缺点,例如认证报⽂被攻击者拦截到攻击者可以获取到资源。
3. X.509:证书认证,X.509是⼀种⾮常通⽤的证书格式,证书包含版本号、序列号(唯⼀)、签名、颁发者、有效期、主体、主体公钥。
4. LDAP:轻量级⽬录访问协议(Lightweight Directory Access Protocol)。
5. Form:基于表单的认证模式。
1.2 权限拦截
图1-2 ⽤户请求
图1-3 过滤器
Spring Security提供了很多过滤器,其中SecurityContextPersistenceFilter、UsernamePasswordAuthenticationFilter、FilterSecurityInterceptor分别对
应SecurityContext、AuthenticationManager、AccessDecisionManager的处理。
图1-4 Spring Security过滤链流程图下⾯分别介绍各个过滤器的功能。
过滤器描述WebAsyncManagerIntegrationFilter
设置SecurityContext 到异步线程中,⽤于获取⽤户上下⽂信息SecurityContextPersistenceFilter 整个请求过程中SecurityContext 的创建和清理1.未登录,SecurityContext 为null ,创建⼀个新的ThreadLocal 的SecurityContext 填充SecurityContextHolder .
2.已登录,从SecurityContextRepository 获取的SecurityContext 对象.
两个请求完成后都清空SecurityContextHolder ,并更新SecurityContextRepository
HeaderWriterFilter 添加头信息到响应对象
CsrfFilter 防⽌csrf 攻击(跨站请求伪造)的过滤器
LogoutFilter 登出处理
UsernamePasswordAuthenticationFilter 获取表单⽤户名和密码,处理基于表单的登录请求
DefaultLoginPageGeneratingFilter 配置登录页⾯
BasicAuthenticationFilter 检测和处理http basic 认证,将结果放进SecurityContextHolder
RequestCacheAwareFilter 处理请求request 的缓存
SecurityContextHolderAwareRequestFilter 包装请求request ,便于访问SecurityContextHolder AnonymousAuthenticationFilter 匿名⾝份过滤器,不存在⽤户信息时调⽤该过滤器
SessionManagementFilter 检测有⽤户登录认证时做相应的session 管理
ExceptionTranslationFilter 处理AccessDeniedException 访问异常和AuthenticationException 认证异常
FilterSecurityInterceptor
检测⽤户是否具有访问资源路径的权限过滤器
描述1.3
数据库管理
图1-5 Spring Security 核⼼处理流程
上图展⽰的Spring Security 核⼼处理流程。当⼀个⽤户登录时,会先进⾏⾝份认证,如果⾝份认证未通过会要求⽤户重新认证,当⽤户⾝份证通过后就会调⽤⾓⾊管理器判断他是否可以访问,这⾥,如果要
实现数据库管理⽤户及权限,就需要⾃定义⽤户登录功能,Spring Security 已经提供好了⼀个接⼝UserDetailsService 。
package org.userdetails;
public interface UserDetailsService {
/**
* Locates the user based on the username. In the actual implementation, the search
* may possibly be case sensitive, or case insensitive depending on how the
* implementation instance is configured. In this case, the <code>UserDetails</code>
* object that comes back may have a username that is of a different case than what
* was actually requested..
*
* @param username the username identifying the user whose data is required.
*
* @return a fully populated user record (never <code>null</code>)
*
* @throws UsernameNotFoundException if the user could not be found or the user has no
* GrantedAuthority
*/
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
UserDetailService 该接⼝只有⼀个⽅法,通过⽅法名可以看出⽅法是通过⽤户名来获取⽤户信息的,但返回结果是UserDetails 对象,UserDetails 也是⼀个接⼝,接⼝中任何⼀个⽅法返回false ⽤户的凭证就会被视为⽆效。
package org.userdetails;
import org.Authentication;
import org.GrantedAuthority;
import java.io.Serializable;
import java.util.Collection;
/**
* Provides core user information.
*
* <p>
* Implementations are not used directly by Spring Security for security purposes. They
* simply store user information which is later encapsulated into {@link Authentication}
* objects. This allows non-security related user information (such as email addresses,
* telephone numbers etc) to be stored in a convenient location.
* <p>
* Concrete implementations must take particular care to ensure the non-null contract
* detailed for each method is enforced. See
* {@link org.userdetails.User} for a reference
* implementation (which you might like to extend or use in your code).
*
* @see UserDetailsService
* @see UserCache
*
* @author Ben Alex
*/
public interface UserDetails extends Serializable {
// ~ Methods
// ========================================================================================================
/**
* Returns the authorities granted to the user. Cannot return <code>null</code>.
*
* @return the authorities, sorted by natural key (never <code>null</code>)
*/
Collection<? extends GrantedAuthority> getAuthorities(); //权限集合
/**
* Returns the password used to authenticate the user.
*
* @return the password
*/
String getPassword(); //密码
/**
* Returns the username used to authenticate the user. Cannot return <code>null</code>.
*
* @return the username (never <code>null</code>)
*/
String getUsername(); //⽤户名
/
**
* Indicates whether the user's account has expired. An expired account cannot be
* authenticated.
*
* @return <code>true</code> if the user's account is valid (ie non-expired),
* <code>false</code> if no longer valid (ie expired)
*/
boolean isAccountNonExpired(); //账户是否过期
/**
* Indicates whether the user is locked or unlocked. A locked user cannot be
* authenticated.
*
* @return <code>true</code> if the user is not locked, <code>false</code> otherwise
*/
boolean isAccountNonLocked(); //账户是否被锁定
/**
* Indicates whether the user's credentials (password) has expired. Expired
* credentials prevent authentication.
*
* @return <code>true</code> if the user's credentials are valid (ie non-expired),
* <code>false</code> if no longer valid (ie expired)
*/
boolean isCredentialsNonExpired(); //证书是否过期
/**
* Indicates whether the user is enabled or disabled. A disabled user cannot be
* authenticated.
*
* @return <code>true</code> if the user is enabled, <code>false</code> otherwise
*/
boolean isEnabled(); //账户是否有效
}
这⾥需要注意的是Authentication与UserDetails对象的区分,Authentication对象才是Spring Security使⽤的进⾏安全访问控制⽤户信息的安全对象,实际
上Authentication对象有未认证和已认证两种状态,在作为参数传⼊认证管理器的时候,它是⼀个为认证的对象,它从客户端获取⽤户的⾝份认证信息,如⽤户名、密码,可以是从⼀个登录页⾯,也可以是从cookie中获取,并由系统⾃动⽣成⼀个Authentication对象,⽽这⾥的UserDetails代表的是⼀个⽤户安全信息的源,这个源可以是从数据库、LDAP服务器、CA中⼼返回,Spring Security要做的就是将未认证的Authentication对象与UserDetails对象进⾏匹配,成功后将UserDetails对象中的权限信息拷贝到Authentication中,组成⼀个完整的Authentication对象,与其他组件进⾏共享。
package org.;
import java.io.Serializable;
import java.security.Principal;
import java.util.Collection;
import org.springframework.security.authentication.AuthenticationManager;
import org.ontext.SecurityContextHolder;
public interface Authentication extends Principal, Serializable {
/**权限集合*/
Collection<? extends GrantedAuthority> getAuthorities();
/**获取凭证*/
Object getCredentials();
/**获取认证⼀些额外信息*/
Object getDetails();
/**过去认证的实体*/
Object getPrincipal();
/**是否认证通过*/
boolean isAuthenticated();
/**
* See {@link #isAuthenticated()} for a full description.
* <p>
* Implementations should <b>always</b> allow this method to be called with a
* <code>false</code> parameter, as this is used by various classes to specify the
* authentication token should not be trusted. If an implementation wishes to reject
* an invocation with a <code>true</code> parameter (which would indicate the
* authentication token is trusted - a potential security risk) the implementation
* should throw an {@link IllegalArgumentException}.
*
* @param isAuthenticated <code>true</code> if the token should be trusted (which may
* result in an exception) or <code>false</code> if the token should not be trusted
*
* @throws IllegalArgumentException if an attempt to make the authentication token
* trusted (by passing <code>true</code> as the argument) is rejected due to the
* implementation being immutable or implementing its own alternative approach to
* {@link #isAuthenticated()}
*/
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
了解了Spring Security的上⾯三个对象,当我们需要数据库管理⽤户时,我们需要⼿动实现UserDetailsService对象中的loadUserByUsername⽅法,这就需要我们同时准备以下⼏张数据表,分别是⽤户表(user)、⾓⾊表(role)、权限表(permission)、⽤户和⾓⾊关系表(user_role)、权限和⾓⾊关系表(permission_role),UserDetails中的⽤户状态通过⽤户表⾥的属性去填充,UserDetails
中的权限集合则是通过⾓⾊表、权限表、⽤户和⾓⾊关系表、权限和⾓⾊关系表构成的RBAC模型来提供,这样就可以把⽤户认证、⽤户权限集合放在数据库中进⾏管理了。
1.4 权限缓存
Spring Security的权限缓存和数据库管理有关,都是在⽤户认证上做⽂章,所以都与UserDetails有关,与数据库管理不同的是,Spring Security提供了⼀个可以缓存UserDetailsService的实现类,这个类的名字是CachingUserDetailsService
package org.springframework.security.authentication;
import org.userdetails.UserCache;
import org.userdetails.UserDetails;
import org.userdetails.UserDetailsService;
import org.userdetails.cache.NullUserCache;
import org.springframework.util.Assert;
/
**
*
* @author Luke Taylor
* @since 2.0
*/
public class CachingUserDetailsService implements UserDetailsService {
private UserCache userCache = new NullUserCache();
private final UserDetailsService delegate;
public CachingUserDetailsService(UserDetailsService delegate) {
this.delegate = delegate;
}
public UserCache getUserCache() {
return userCache;
}
public void setUserCache(UserCache userCache) {
this.userCache = userCache;
}
public UserDetails loadUserByUsername(String username) {
UserDetails user = UserFromCache(username);
//缓存中不存在UserDetails时,通过UserDetailsService加载
if (user == null) {
user = delegate.loadUserByUsername(username);
}
+ " returned null for username " + username + ". "
+ "This is an interface contract violation");
//将UserDetials存⼊缓存,并将UserDetails返回
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论