目录
前后端分离下,security如何实现认证功能(token)?
如何在 Spring Security 中实现 JWT 认证?
Springsecurity
核心功能
认证、授权、加密、会话管理
什么是Spring Security核心功能?
Spring Security是一个基于Spring框架的安全框架,提供了完整的安全解决方案,包括认证、授权、攻击防护等功能。
其核心功能包括:
认证:提供了多种认证方式,如表单认证、HTTP Basic认证、OAuth2认证等,可以与多种身份验证机制集成。
授权:提供了多种授权方式,如角色授权、基于表达式的授权等,可以对应用程序中的不同资源进行授权。
攻击防护:提供了多种防护机制,如跨站点请求伪造(CSRF)防护、注入攻击防护等。
会话管理:提供了会话管理机制,如令牌管理、并发控制等。
监视与管理:提供了监视与管理机制,如访问日志记录、审计等。
Spring Security通过配置安全规则和过滤器链来实现以上功能,可以轻松地为Spring应用程序提供安全性和保护机制。
http.csrf().disable() 禁用csrf攻击防护
http.csrf().enabled() 启用csrf攻击防护
CSRF是指跨站请求伪造(Cross-site request forgery),是web常见的攻击之一。
前后端分离下,security如何实现认证功能(token)?
利用token机制实现
Spring Security的具体工作原理如下:
1.用户请求Web应用程序的受保护资源。
2.Spring Security拦截请求,并尝试获取用户的身份验证信息。
3.如果用户没有经过身份验证,Spring Security将向用户显示一个登录页面,并要求用户提供有效的凭据(用户名和密码)。
4.一旦用户提供了有效的凭据,Spring Security将验证这些凭据,并创建一个已认证的安全上下文(SecurityContext)对象。
5.安全上下文对象包含已认证的用户信息,包括用户名、角色和授权信息。
6.在接下来的请求中,Spring Security将使用已经认证的安全上下文对象来判断用户是否有权访问受保护的资源。
7.如果用户有权访问资源,Spring Security将允许用户访问资源,否则将返回一个错误信息。
security内部的执行原理?
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看 入门案例中的过滤器。
Spring Security 的核心功能是通过一系列内置的过滤器(Filter)组成的过滤器链(FilterChain)实现的。这些过滤器按照特定顺序执行,分别处理认证、授权、会话管理、CSRF防护等安全任务。以下是 Spring Security 主要的内置过滤器及其作用:
1. 核心过滤器(按典型执行顺序)
-
ChannelProcessingFilter
-
强制请求使用 HTTPS(配置了
requiresChannel()
时生效)。
-
-
WebAsyncManagerIntegrationFilter
-
集成 WebAsyncManager,确保异步请求也能持有安全上下文。
-
-
SecurityContextPersistenceFilter
-
在请求开始时从
SecurityContextRepository
(默认从 HTTP Session)加载安全上下文(SecurityContext
),并在请求结束后保存。
-
-
HeaderWriterFilter
-
写入安全相关的 HTTP 头(如
X-Content-Type-Options
,X-Frame-Options
)。
-
-
CorsFilter
-
处理跨域请求(CORS),需显式配置。
-
-
CsrfFilter
-
防护 CSRF 攻击,默认对非幂等请求(如 POST)校验
_csrf
令牌。
-
-
LogoutFilter
-
处理注销请求(默认匹配
/logout
),清除认证信息。
-
-
OAuth2AuthorizationRequestRedirectFilter
-
OAuth2 登录时重定向到授权端点(如第三方登录)。
-
-
Saml2WebSsoAuthenticationRequestFilter
-
SAML2 身份验证的重定向逻辑。
-
-
X509AuthenticationFilter
-
处理基于 X.509 证书的认证。
-
-
AbstractPreAuthenticatedProcessingFilter
-
预认证处理(如从请求头提取认证信息)。
-
-
UsernamePasswordAuthenticationFilter
-
处理表单登录(默认匹配
/login
,提交用户名密码)。
-
-
DefaultLoginPageGeneratingFilter
-
自动生成默认登录页面(未自定义时生效)。
-
-
DefaultLogoutPageGeneratingFilter
-
自动生成默认注销页面。
-
-
ConcurrentSessionFilter
-
检查会话是否过期(需配置会话管理)。
-
-
BearerTokenAuthenticationFilter
-
处理 OAuth2 Bearer Token 认证(JWT 等)。
-
-
RequestCacheAwareFilter
-
缓存请求(如登录成功后重定向到原始请求)。
-
-
SecurityContextHolderAwareRequestFilter
-
包装请求对象,提供 Spring Security 增强方法(如
isUserInRole()
)。
-
-
AnonymousAuthenticationFilter
-
为未认证请求分配一个匿名
Authentication
对象。
-
-
SessionManagementFilter
-
处理会话固定(session fixation)防护和并发会话控制。
-
-
ExceptionTranslationFilter
-
转换安全异常(如
AuthenticationException
重定向到登录页,AccessDeniedException
返回 403)。
-
-
FilterSecurityInterceptor
-
最终权限检查(基于
ConfigAttribute
判断请求是否允许访问)。
-
-
AuthorizationFilter
(Spring Security 5.5+ 替代FilterSecurityInterceptor
)-
更现代的授权决策实现。
-
security内部实现过程(案例详解)?
1.提交用户名密码
2.封装Authentication对象,这时候最多只有用户名和密码,权限还没有
3.调用authenticate方法进行认证
4.调用DaoAuthenticationProvider的authenticate方法进行认证
5.调用loadUserByUsername方法查询用户
5.1根据用户名去查询对应的用户及这个用户对应的权限信息,InMemoryUserDetailsManager是在内存中查找
5.2把对应的用户信息包括权限信息封装成UserDetails对象
6.返回UserDetails对象
7.通过PasswordEncoder对比UserDetails中的密码和Authentication的密码是否正确(passwordEncoder.matchs("1234","加密后密码"))
8.如果正确就把UserDetials中的权限下信息设置到Authentication对象中
9.返回Authentication对象
10.如果上一步返回了Authentication对象就使用
SecurityContextHolder.getContext().setAuthentication方法存储该对象。
其它过滤器中会通过SecurityContextHolder来获取当前用户信息
有哪些控制请求访问权限的方法?
在Spring Security中,可以使用以下方法来控制请求访问权限:
permitAll():允许所有用户访问该请求,不需要进行任何身份验证。
denyAll():拒绝所有用户访问该请求。
anonymous():允许匿名用户访问该请求。
authenticated():要求用户进行身份验证,但是不要求用户具有任何特定的角色。
hasRole(String role):要求用户具有特定的角色才能访问该请求。
hasAnyRole(String... roles):要求用户具有多个角色中的至少一个角色才能访问该请求。
hasAuthority(String authority):要求用户具有特定的权限才能访问该请求。
hasAnyAuthority(String... authorities):要求用户具有多个权限中的至少一个权限才能访问该请求。
可以将这些方法应用于Spring Security的配置类或者在Spring Security注解中使用。
hasRole 和 hasAuthority 有区别吗?
在Spring Security中,hasRole和hasAuthority都可以用来控制用户的访问权限,但它们有一些细微的差别。
hasRole方法是基于角色进行访问控制的。它检查用户是否有指定的角色,并且这些角色以"ROLE_"前缀作为前缀(例如"ROLE_ADMIN")。
hasAuthority方法是基于权限进行访问控制的。它检查用户是否有指定的权限,并且这些权限没有前缀。
因此,使用hasRole方法需要在用户的角色名称前添加"ROLE_"前缀,而使用hasAuthority方法不需要这样做。
例如,假设用户有一个角色为"ADMIN"和一个权限为"VIEW_REPORTS",可以使用以下方式控制用户对页面的访问权限:
.antMatchers("/admin/").hasRole("ADMIN") .antMatchers("/reports/").hasAuthority("VIEW_REPORTS") 在这个例子中,只有具有"ROLE_ADMIN"角色的用户才能访问/admin/路径下的页面,而具有"VIEW_REPORTS"权限的用户才能访问/reports/路径下的页面。
如何对密码进行加密?
在 Spring Security 中对密码进行加密通常使用的是密码编码器(PasswordEncoder)。PasswordEncoder 的作用是将明文密码加密成密文密码,以便于存储和校验。Spring Security 提供了多种常见的密码编码器,例如 BCryptPasswordEncoder、SCryptPasswordEncoder、StandardPasswordEncoder 等。
以 BCryptPasswordEncoder 为例,使用步骤如下:
1.在 pom.xml 文件中添加 BCryptPasswordEncoder 的依赖:
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>5.6.1</version> </dependency> 2.在 Spring 配置文件中注入 BCryptPasswordEncoder:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// ...
}
常用的方法:
passwordEncoder.encode("1234")
passwordEncoder.matchs("1234","加密后密码")
如何在 Spring Security 中实现 JWT 认证?
要在 Spring Security 中实现基于 JWT(JSON Web Token)的认证,需要执行以下步骤:
-
引入 JWT 相关依赖,如
jjwt
。 -
实现一个用于生成和解析 JWT 的工具类。
-
创建一个自定义的
AuthenticationFilter
,用于从请求头中提取 JWT 并进行认证。 -
在
SecurityConfig
类中配置HttpSecurity
,将自定义的AuthenticationFilter
添加到过滤器链。
以下是一个简单的示例:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
在这个示例中,我们禁用了 CSRF 防护和会话管理,然后将自定义的 JwtAuthenticationFilter
添加到过滤器链。
认证和授权有什么区别?
-
认证(Authentication):是验证用户身份的过程,通常涉及到用户名和密码的检查。成功认证后,用户将获得一个认证令牌,用于后续的授权过程。
-
授权(Authorization):是确定用户是否具有访问特定资源或执行特定操作的权限的过程。系统基于用户的角色、权限或访问控制列表(ACL)来判断用户是否具有访问权限。
请简要介绍 Spring Security 的架构。
Spring Security 的架构主要由以下组件组成:
-
SecurityContextHolder
:存储与当前线程关联的安全上下文。 -
Authentication
:表示用户的认证信息。 -
UserDetails
:表示用户的详细信息。 -
UserDetailsService
:用于加载用户详细信息的接口。 -
AuthenticationManager
:负责处理认证请求。 -
AccessDecisionManager
:负责授权决策。 -
FilterChainProxy
:负责处理 HTTP 请求的过滤器链。 -
SecurityFilterChain
:由一系列安全过滤器组成的链。
什么是 SecurityContextHolder
?
SecurityContextHolder
是一个用于存储与当前线程关联的安全上下文的类。它使用 ThreadLocal
机制来存储 SecurityContext
。SecurityContext
包含了当前用户的认证信息,如 Authentication
对象。
Spingsecurity异常拦截处理
认证异常拦截
/*
自定义认证异常处理器类
*/
@Component
public class MyAuthenticationExceptionHandler implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
ResponseResult responseResult = new
ResponseResult(HttpStatus.NETWORK_AUTHENTICATION_REQUIRED.value(), "认证失败!");
response.getWriter().append(JSON.toJSONString(responseResult));
}
}
权限异常拦截
/**
* 自定义权限拒绝异常处理器
*/
@Component
public class MyAccessDenyHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
ResponseResult responseResult = new
ResponseResult(403, "权限拒绝,没有访问权限!");
response.getWriter().append(JSON.toJSONString(responseResult));
}
}
注册异常拦截器
@Configuration
//启用security的注解支持
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationExceptionHandler myAuthenticationExceptionHandler;
@Autowired
private MyAccessDenyHandler myAccessDenyHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置自定义异常处理器(认证异常、权限拒绝异常)
http.exceptionHandling()
.authenticationEntryPoint(myAuthenticationExceptionHandler)
.accessDeniedHandler(myAccessDenyHandler);
}
相关权限注解
@PreAuthorize("hasAuthority('user:list')")
@PreAuthorize("hasAuthority('system:dept:list')")
@PreAuthorize("hasAnyAuthority('system:dept:list','system:test:list')")
@PreAuthorize("hasRole('CEO')")
@PreAuthorize("hasAnyRole('CEO')")
hasAuthority 和数据库表权限是等值比对
hasRole 添加ROLE_ 之后和数据库表中的角色名字比对
设置跨域访问
@Configuration
public class MyCorsFilter implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") //路径
.allowedOrigins("*") //域名
.allowedMethods("*") //方法 get/post/put/delete
.allowedHeaders("*") //请求头
.allowCredentials(true) ; //cookie 是否允许携带cookie
}
}
MySecurityConfig.java中设置security允许跨域访问。
//设置跨域访问
http.cors();