1. why
spring security 是强大的高度可定制的 身份验证 和 访问控制 应用级框架。
常见的漏洞包含:
身份验证失效,会话固定,跨站脚本xss请求伪造,CSRF注入敏感数据泄漏缺乏方法访问控制。
身份验证失效:
不能仅仅验证用户是否登陆,而要验证检索数据的用户是否是合法用户。
会话固定:
允许攻击者通过重复使用先前生成的会话ID来冒充有效用户。所以每次请求的 token 最好是可变的。
跨站脚本(xss):
允许将客户端脚本注入到服务器公开的 Web 服务中,从而允许其他用户运行这些服务。恶意脚本等等。
跨站请求伪造(CSRF):
注入攻击:
注入客户端脚本、SQL注入、XPath注入、操作系统命令注入、LDAP注入等等。要对用户的行为进行限制,输入内容进行校验,不能让他们有对系统进行损害的操作。比如 mybatis sql 注入,linux 命令 cd 到上一个目录,删除其他用户目录文件等等。
处理敏感数据的暴漏:
机密数据的泄漏,比如访问用户的时候,返回什么信息。本人登陆的时候可以返回用户名,手机号等等,但是其他相关用户只能看到它的用户名。公开的一些文件和数据集,不允许进行写操作等等。一些异常信息,可能会暴漏代码结构等等。
使用具有已知漏洞的依赖:
比如我们使用的包版本包含漏洞,那么我们就要及时更新来修复漏洞。
通过 spring security 来解决以上的问题。
2. demo
OAuth2授权流程:
用户访问应用程序中的用例,需要调用后端资源,必须获得访问权限 token ,发送用户凭据或刷新令牌。
如果凭据或者刷新令牌正确,授权服务器返回一个访问令牌给客户端。
向资源服务器请求的标头使用访问令牌时调用所需的资源。
一般401用于失败的身份验证,403意味着服务器识别了调用者请求,但是没有调用需要的权限。
spring security身份验证过程中各种组件之间的关系:
请求被过滤器拦截(authentication filter)——> 认证委托给(authentication manager)——> manager 使用(authentication provider)来实现身份验证——>provider 找到用户的详细信息,并使用密码校验验证密码——>认证结果返回给过滤器——>有关身份验证的实体详细信息存储在 security 上下文中。
3. 管理用户
spring security 提供了 UserDetails 合约,必须实现该接口来描述用户,User 里面需要添加角色字段用于 security。
代码举例:
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles().stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
UserDetailsService
在身份验证框架中需要实现的契约,用于返回用户的详细信息,通过名称查找用户。
UserDetail
合约描述了用户。用户拥有一个或者多个权限,由 GrantedAuthority
接口表示,SimpleGrantedAuthority
是其中一种实现方式。要像用户添加创建、删除或更改密码等操作,UserDetailsManager
扩展 UserDetailsService 来添加操作。
具体代码如下:
package org.springframework.security.core.userdetails;
import java.io.Serializable;
import java.util.Collection;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
/**
* 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.springframework.security.core.userdetails.User} for a reference
* implementation (which you might like to extend or use in your code).
*
* @author Ben Alex
* @see UserDetailsService
* @see UserCache
*/
public interface UserDetails extends Serializable {
/**
* 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();
}
UserDetails 用来描述用户契约安全。可以设置授予用户的权限组 getAuthorities(),通常采用字符串格式返回 ,也可以返回用户名和密码,让账户是否过期,是否锁定账户,让凭证过期,禁用账户等等,对应上面源码的所有方法,具体可以看源码里面的注释。
一般情况下,都设置为 true。
UsernameNotFoundException 直接继承自 AuthenticationException 类型,继承RuntimeException类。
5. 处理密码
PasswordEncoder 密码编码器,对密码进行加密或散列,检查给定的编码字符串是否与明文密码匹配。
6. 身份验证
package org.springframework.security.authentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
* Indicates a class can process a specific
* {@link org.springframework.security.core.Authentication} implementation.
*
* @author Ben Alex
*/
public interface AuthenticationProvider {
/**
* Performs authentication with the same contract as
* {@link org.springframework.security.authentication.AuthenticationManager#authenticate(Authentication)}
* .
* @param authentication the authentication request object.
* @return a fully authenticated object including credentials. May return
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
* authentication of the passed <code>Authentication</code> object. In such a case,
* the next <code>AuthenticationProvider</code> that supports the presented
* <code>Authentication</code> class will be tried.
* @throws AuthenticationException if authentication fails.
*/
Authentication authenticate(Authentication authentication) throws AuthenticationException;
/**
* Returns <code>true</code> if this <Code>AuthenticationProvider</code> supports the
* indicated <Code>Authentication</code> object.
* <p>
* Returning <code>true</code> does not guarantee an
* <code>AuthenticationProvider</code> will be able to authenticate the presented
* instance of the <code>Authentication</code> class. It simply indicates it can
* support closer evaluation of it. An <code>AuthenticationProvider</code> can still
* return <code>null</code> from the {@link #authenticate(Authentication)} method to
* indicate another <code>AuthenticationProvider</code> should be tried.
* </p>
* <p>
* Selection of an <code>AuthenticationProvider</code> capable of performing
* authentication is conducted at runtime the <code>ProviderManager</code>.
* </p>
* @param authentication
* @return <code>true</code> if the implementation can more closely evaluate the
* <code>Authentication</code> class presented
*/
boolean supports(Class<?> authentication);
}
AuthenticationProvider 实现的自定义身份验证流程。为了验证身份验证请求,AuthenticationProvider 使用提供的 UserDetailsService 实现加载用户详细信息,如果密码匹配,则使用 PasswordEncoder 验证密码。
如果用户不存在或密码不正确,则 AuthenticationProvider 会抛出 AuthenticationException 。
身份验证成功后,身份验证过滤器将经过身份验证的实体的详细信息存储在安全上下文中。从那里,实现映射到请求的操作的控制器可以在需要时访问这些详细信息。
SecurityContext 的主要职责是存储 Authentication 对象。它是如何管理的呢?Spring Security提供了三种策略来使用充当管理者角色的对象来管理SecurityContext。
SecurityContextHolder:
MODE_THREADLOCAL:允许每个线程在安全上下文中存储它的 Security context 。
MODE_INHERITABLETHREADLOCAL:异步方法下将安全上下文复制到下一个线程。
MODE_GLOBAL:所有线程看到相同的安全性上下文实例。
http basic
http/successHandler/failureHandler 基于 http 可以自行设置 security 的拦截接口,权限,等等。
6. 实践:小型安全web应用程序
7. 配置授权:闲置访问
授权是系统决定所识别的客户端是否有权访问所请求资源的过程,总是在身份验证后发生。
客户端发出请求,身份认证过滤器进行身份验证。身份验证成功后,身份验证过滤器将用户详细信息存储在安全上下文中,并将请求转发到授权过滤器。授权过滤器决定是否允许调用这些请求。授权过滤器,通过安全上下文来判断是否授权该请求。
成功验证用户身份后,应用程序使用 GrantedAuthority 接口表示的权限进行授权。
8. 配置授权:应用限制
选择应用授权配置请求,使用匹配器方法。共有三种,如下所示:
mvc匹配器
举例:
http.authorizeRequests() .mvcMatchers(/hello).hasRole(ADMIN)
.mvcMatchers(/ciao).hasRole(MANAGER);
.anyRequest().permitAll(); 允许其他请求,无需身份验证。
.anyRequest().authenticated(); 所有其他请求,只能由经过身份验证的用户访问。
mvcMatchers: 允许您指定应用限制的 HTTP 方法和路径。
http.csrf(AbstractHttpConfigurer::disable).cors(Customizer.withDefaults()):禁用 CSRF(跨站请求伪造)防护并启用 CORS(跨源资源共享)的默认设置。只有禁用 CSRF 才能执行 Post 操作。
http.csrf(AbstractHttpConfigurer::disable):这部分代码禁用了 CSRF 防护。在 Web 应用中,CSRF 是一种攻击手段,攻击者通过诱导用户点击链接或者加载页面的方式,使得用户在不知情的情况下向网站发出恶意请求。Spring Security 默认开启 CSRF 防护,但在某些情况下,你可能需要禁用它,比如说在使用无状态的认证机制(如 JWT)时。
cors(Customizer.withDefaults()):这部分代码启用了 CORS 的默认设置。CORS 是一种浏览器技术,允许一个网页的脚本来访问另一个源(协议 + 域名 + 端口)的资源。默认设置通常包括允许所有源、所有 HTTP 方法和所有头部信息。如果你需要更精细的控制,你可以提供自己的 CorsConfigurationSource 或 CorsConfiguration。
ant匹配器
正则表达式匹配器
mvc
跟ant
相比更加推荐mvc
,正则表达式比较复杂,可读性不好,是最后的选择。
9. 实施过滤器
过滤器拦截请求,在其应用职责后,将请求委托给链中的下一个过滤器。过滤器会委托责任到管理器对象。
任何过滤器对象,需要重写 doFilter()方法来实现其逻辑,参数为HttpServletRequest【http请求】, HttpServletResponse【http响应】, FilterChain【过滤器链】。
http.addFilterBefore( newRequestValidationFilter(),BasicAuthenticationFilter.class) 在身份验证之前添加过滤器。
http…addFilterAfter( AuthenticationLoggingFilter(),BasicAuthenticationFilter.class) 在身份验证之后添加过滤器。
spring security 提供了 OncePerRequestFilter , 能保证过滤器的 doFilter 方法仅被调用1次。
10. 应用CSRF保护和CORS
应用跨站请求伪造(CSRF)保护
跨源资源共享(CORS)
11. 实践:职责分离
JWT(JSON Web Token)分为标头,正文,签名。JSON 代表它传输的数据格式,Web 它用于 web 请求,Token它是一种 token 实现。
12. OAuth2如何工作
OAuth 2 通常用于保护 Web 应用程序,被称为授权框架,其主要目的是允许第三方网站或应用程序访问资源。
OAuth 2 包含:
资源服务器:托管用户拥有的资源的应用程序。
用户:拥有资源的个人。
客户端: 访问用户拥有的资源的应用程序。
授权服务器:授权客户端访问的应用程序。
常见的OAuth 2 授权:
授权码:授权码授予类型。客户端要求用户直接与授权服务器交互,以授予其对用户请求的权限。授权后,授权服务器会颁发一个令牌,客户端使用该令牌来访问用户的资源。
密码:密码授予类型假设用户与客户端共享其凭据。客户端使用这些从授权服务器获取令牌。然后它访问资源代表用户从资源服务器获取。
客户端凭证:客户端凭据授予类型。如果客户端需要访问资源但不代表资源所有者,我们将使用此流程。该资源可以是不属于用户的端点,拥有客户凭证。
13. OAuth2:实现授权服务器
授权类型:1. 授权码 2.密码 3.客户端凭证
授权服务器存储用户和客户端凭证。它使用客户端凭证,以便只允许已知的应用程序获得其授权。
14. OAuth2:实现资源服务器
服务器验证来自客户端端令牌:
- 直接调用授权服务器。
- 授权服务器和资源服务器共享数据库。存储在数据库中颁发令牌,资源服务器读取令牌进行验证。
15. 使用JWT和加密签名
JWT包含:标头、主体、签名。header 和 body 中的详细信息用 JSON 表示,并且它们是 Base64 编码的。第三部分是签名,使用加密算法生成,该算法使用标头和主体作为输入。
非对称加密:非对称加密有两个密钥,一个公钥,一个私钥。授权服务器使用私钥对令牌进行签名。公钥用于验证签名。
16. 全局方法安全性:授权前和授权后
启用全局方法安全性:
调用授权,根据权限决定某人是否可以调用某个方法;过滤,决定方法可以通过其参数接收什么。