一,了解SpringSecurity
1.1SpringSecurity是spring中的一个安全管理框架,具有强大的安全管理和自定义配置功能,并且是一个高度可定制的用于身份验证和访问的控制框架,作为Spring中的一个框架它致力于对Java程序进行身份验证和授权,和所有的Spring框架一样他的强大之处在于他的轻松扩展和满足自定义的要求。
在Java生态之中有 SpringSecurity和ApacheShiro 两个安全框架,都可以完成认证和授权服务
在SpringSeciurity官网中有这样的介绍
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实标准。
一般web应用都包含认证和授权:
认证:验证用户是否是本系统的内部用户,并且确认具体是哪一个用户
授权:经过认证后的用户是否有权限进行某个操作
1.2,简单罗列SpringSecurity于Shiro异同点:
相同点:
-
提供安全特性: 两者都提供了身份验证、授权、加密等安全功能,可以帮助开发人员保护应用程序的安全。
-
支持多种认证方式: Spring Security 和 Shiro 都支持多种认证机制,包括基于表单、HTTP 基本认证、OAuth 等,以满足不同场景下的需求。
-
易于扩展和定制: 两者都具有良好的扩展性和可定制性,可以根据实际需求灵活地配置和定制安全策略。
不同点:
-
生态环境: Spring Security 是 Spring 框架的一部分,与其他 Spring 组件集成得更加紧密;而 Apache Shiro 是独立的安全框架,可以单独使用,与 Spring 框架无关。
-
复杂度: 相对而言,Spring Security 提供更为复杂的功能,适合于大型企业级应用程序的安全需求;Apache Shiro 则更加轻量级和简单,适合于中小型应用或快速开发的场景。
-
学习曲线: 由于 Spring Security 的功能更为丰富和复杂,学习曲线可能会比较陡峭,需要花费更多时间来掌握;而 Apache Shiro 设计上更加简洁直观,学习起来相对容易。
所以一般对于spring中的项目一般使用SpringSecurity框架进行身份的认证和授权
1.3一般SpringSecurity的执行流程一般为以下七步:
-
发起认证请求: 用户在客户端(如浏览器)输入用户名和密码并提交表单,或者使用其他认证方式(如 JWT)向应用程序发起认证请求。
-
进行身份验证: Spring Security 拦截认证请求,并根据配置的认证逻辑对用户进行身份验证,例如检查用户名密码是否匹配等。
-
生成认证令牌: 验证通过后,Spring Security 会生成一个认证令牌(Authentication Token),表示该用户已被成功认证。
-
授权访问: Spring Security 根据用户的认证信息,检查用户是否有权限执行所请求的操作,即授权访问,包括对 URL、接口或方法的访问权限控制。
-
处理认证成功/失败: 根据认证结果,Spring Security 将请求重定向到相应的页面,比如认证成功后跳转到指定页面,认证失败则返回登录页面或错误信息。
-
创建安全上下文: 在整个用户会话期间,Spring Security 会创建一个安全上下文(Security Context),保存用户的认证信息,并确保安全上下文在每次请求中都能被正确管理和更新。
-
保护资源: 一旦用户成功认证和授权访问,Spring Security 将允许用户访问所请求的资源,确保系统的各项功能按照设定的安全规则运行。
二,OAthu2
OAuth 2.0 是一种授权框架,用于允许第三方应用程序在不获取用户凭证的情况下访问受保护的资源。OAuth 2.0 的设计目标是解决互联网上应用程序之间的安全性和授权问题,同时提供了一种标准化的授权流程。
以下是 OAuth 2.0 的一些核心概念和角色:
-
资源拥有者(Resource Owner): 拥有受保护的资源(如用户数据)的实体,可以是最终用户。
-
客户端(Client): 请求访问受保护资源的应用程序,可以代表资源拥有者请求授权访问资源。
-
授权服务器(Authorization Server): 负责颁发访问令牌给客户端,进行用户身份验证和授权操作。
-
资源服务器(Resource Server): 存储及响应受保护资源请求的服务器,验证访问令牌并提供受保护资源。
-
访问令牌(Access Token): 授权服务器颁发给客户端的令牌,用于访问资源服务器中的受保护资源。
-
刷新令牌(Refresh Token): 客户端可以使用刷新令牌来获取新的访问令牌,通常用于延长访问令牌的有效期。
OAuth 2.0 定义了多种授权方式,包括授权码授权、密码授权、客户端凭证授权和隐式授权等,每种方式适用于不同的场景和需求。OAuth 2.0 的授权流程一般包括以下步骤:
-
客户端注册: 客户端向授权服务器注册,并获得客户端 ID 和密钥。
-
授权请求: 客户端重定向用户到授权服务器,请求授权。
-
用户认证: 用户登录并授权客户端访问其受保护资源。
-
颁发访问令牌: 授权服务器颁发访问令牌给客户端。
-
访问资源: 客户端使用访问令牌请求访问资源服务器中的受保护资源。
通过 OAuth 2.0,客户端可以安全地获取访问受保护资源的权限,而无需直接持有用户凭证,从而提高了系统的安全性和用户体验。OAuth 2.0 已经成为互联网上广泛使用的授权框架,在各种应用场景中都有着重要的作用。
三,在我们springboot项目的基础上添加Spring Security OAthu框架进行安全访问和授权的实例:
3.1添加SpringSecurity及OAthu2框架依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
3.2添加框架启动类
@SpringBootApplication
@EnableResourceServer
@EnableDiscoveryClient
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
这个实际上不仅仅是SpringSecurity的一个启动类,它把包含了多个微服务其中
-
@EnableGlobalMethodSecurity
用于在 Spring Security 中启用方法级的安全性控制,允许在方法级别上配置权限控制。 -
prePostEnabled = true
参数表示启用基于表达式的前置和后置注解。这意味着可以在使用@PreAuthorize
和@PostAuthorize
注解时进行方法级的安全性检查
Spring Security进行用户验证的流程图:
-
UsernamePasswordAuthenticationFilter
主要处理基于用户名和密码的认证方式,负责将请求信息封装成Authentication
对象。 -
Authentication
接口封装了用户相关信息,代表了一个身份验证请求。 -
AuthenticationManager
接口定义了认证Authentication
的方法,是认证的核心接口,内部可能会维护多种认证方式的列表,由ProviderManager
实现。 -
AuthenticationProvider
实现了具体的认证逻辑,例如DaoAuthenticationProvider
用于解析并认证UsernamePasswordAuthenticationToken
。 -
UserDetailsService
接口负责根据用户名从数据库中获取用户信息,在实际应用中,需要提供该接口的实现来支持用户信息的加载。 -
UserDetails
接口提供了用户的核心信息,包括用户名、密码等,这些信息被封装到Authentication
对象中进行认证。
其实一整个过滤器链进行控制访问,而这些多滤器可以进行重写所以成就了Spring Security的高定制
查看源码spring security有16个过滤器 下面罗列常见的十个过滤器及其功能:
-
SecurityContextPersistenceFilter: 用于加载用户的安全上下文信息,并将其存储在
SecurityContextHolder
中供后续使用。 -
UsernamePasswordAuthenticationFilter: 处理基于用户名和密码的认证请求,生成一个
UsernamePasswordAuthenticationToken
对象。 -
BasicAuthenticationFilter: 处理 HTTP Basic 认证请求。
-
LogoutFilter: 处理退出登录请求,清除用户的身份验证信息。
-
AnonymousAuthenticationFilter: 在用户未经身份验证时提供匿名身份验证。
-
ExceptionTranslationFilter: 处理 Spring Security 抛出的各种异常,并根据异常类型选择相应的处理策略。
-
FilterSecurityInterceptor: 基于访问控制规则(如 URL 规则)决定是否允许请求通过。
-
CsrfFilter: 防止跨站请求伪造攻击。
-
SessionManagementFilter: 管理用户会话,如控制并发登录数、失效处理等。
-
RememberMeAuthenticationFilter: 处理 Remember-Me 认证请求。
3.3从数据库获取用户信息并进行封装:
@Service
public class MongoUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository repository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return repository.findById(username).orElseThrow(()->new UsernameNotFoundException(username));
}
}
3.4,创建和配置Spring Security中的AuthenticationManager bean。在Spring Security中,AuthenticationManager负责处理认证请求,验证用户的身份。:
package com.piggymetrics.auth.config;
import com.piggymetrics.auth.service.security.MongoUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author cdov
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MongoUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests().anyRequest()
//已认证的请求会被自动授权
.authenticated()
.and()
.csrf().disable();
// @formatter:on
}
// 这段代码展示了一个Spring Security的配置,用于配置HTTP请求的安全性规则。在这里,通过重写configure(HttpSecurity http)方法,开发人员可以定义需要认证的请求规则。
//
// 具体来说,这段代码做了以下事情:
//
// 使用.authorizeRequests()定义了对请求的授权规则,其中.anyRequest().authenticated()表示所有的请求都需要经过认证才能访问。
// 使用.csrf().disable()关闭了跨站请求伪造(CSRF)保护,这意味着在Web应用中将不再进行CSRF检查。
// 在整个配置过程中,// @formatter:off 和 // @formatter:on 注释用于告诉IDE或其他工具不要格式化这部分代码,以确保配置的可读性。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
//使用.userDetailsService(userDetailsService)指定了用于验证用户的自定义用户详情服务(UserDetailsService),这意味着系统将使用该服务来获取用户的详细信息以进行身份验证。
// 调用.passwordEncoder(new BCryptPasswordEncoder())来设置密码编码器为BCryptPasswordEncoder,这表示在存储和比较密码时会使用BCrypt算法进行加密和解密
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
//这段代码是一个方法覆盖,并且使用了@Bean注解,用于创建和配置Spring Security中的AuthenticationManager bean。在Spring Security中,AuthenticationManager负责处理认证请求,验证用户的身份。
//
//具体来说,这段代码做了以下事情:
//
//通过@Override注解,表示该方法是对父类的方法进行重写。
//使用@Bean注解,表明这是一个Spring bean定义的方法,Spring会将其实例化并管理。
//方法返回一个AuthenticationManager类型的对象,并调用super.authenticationManagerBean()来获取由父类提供的AuthenticationManager bean。
//通过这段代码,开发人员可以在Spring应用程序中注入并使用AuthenticationManager,以便在需要时进行用户身份验证。这种方法通常在配置类中使用,用于暴露AuthenticationManager bean给其他组件和服务。
3.5配置 OAuth2 授权服务器的类 OAuth2AuthorizationConfig
:
package com.piggymetrics.auth.config;
import com.piggymetrics.auth.service.security.MongoUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* @author cdov
*/
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
//继承了AuthorizationServerConfigurerAdapter,用于配置授权服务器的行为
private TokenStore tokenStore = new InMemoryTokenStore();
private final String NOOP_PASSWORD_ENCODE = "{noop}";
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private MongoUserDetailsService userDetailsService;
@Autowired
private Environment env;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// TODO persist clients details
// @formatter:off
clients.inMemory()
.withClient("browser")
.authorizedGrantTypes("refresh_token", "password")
.scopes("ui")
.and()
.withClient("account-service")
.secret(env.getProperty("ACCOUNT_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("server")
.and()
.withClient("statistics-service")
.secret(env.getProperty("STATISTICS_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("server")
.and()
.withClient("notification-service")
.secret(env.getProperty("NOTIFICATION_SERVICE_PASSWORD"))
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("server");
// @formatter:on
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
//用于配置授权服务器的端点。在这个示例中,指定了令牌存储方式、认证管理器以及用户详细信息服务
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
//通过调用tokenKeyAccess()和checkTokenAccess()来设置对令牌端点的访问权限,
// 同时使用NoOpPasswordEncoder.getInstance()来指定密码编码器
}
四,总结
Spring Security 是专门用于在 Spring 应用程序中提供身份验证和访问控制的框架,它可以帮助开发人员实现应用程序的安全性需求。而 OAuth 2.0 是一种授权框架,用于授权第三方应用程序访问受保护的资源,而无需用户提供其凭证。
Spring Security:
- 功能: 提供身份验证、授权、会话管理等安全功能。
- 特点: 可以与任何基于 Spring 的应用程序集成,支持多种认证机制和自定义配置。
- 核心组件: 包括
UserDetailsService
、AuthenticationManager
、FilterChainProxy
等。 - 配置方式: 通过配置安全规则、过滤器链、认证提供者等来实现安全功能。
- 用途: 用于保护应用程序的数据和资源,确保只有经过授权的用户才能访问。
OAuth 2.0:
- 功能: 提供授权框架,允许第三方应用程序安全地获取对受保护资源的访问权限。
- 特点: 分离了授权和认证,使得用户不必直接提供凭证给第三方应用。
- 核心角色: 包括资源拥有者、客户端、授权服务器、资源服务器等。
- 授权方式: 支持多种授权方式,如授权码授权、密码授权、客户端凭证授权等。
- 用途: 用于在分布式系统和互联网上实现安全的第三方应用程序授权访问资源的流程。
综上所述,Spring Security 适用于内部应用程序的身份验证和授权控制,而 OAuth 2.0 则适用于在互联网上实现第三方应用程序访问受保护资源的授权场景。结合使用时,Spring Security 可以作为实现 OAuth 2.0 授权服务器的基础框架之一,帮助实现更加安全和可靠的授权流程。