Spring Security从过滤器到认证授权的源码分析
Spring Security从过滤器到认证授权的源码分析
? Spring Security的实现包括认证(Authentication) 和 授权(Authorization)全部都是通过过滤器实现的,源码分析最后都会追寻到源头过滤器。
一、过滤器
1、WebSecurityConfigurerAdapter类
? 一般情况下,实现认证我们需要继承WebSecurityConfigurerAdapter
类,例如,下面的SecurityConfig
是一个前后端分离的SpringSecurity
配置类:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private JwtAuthenticationEntryPoint authenticationErrorHandler;
@Resource
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Resource
private ApplicationContext applicationContext;
@Resource
private JwtTokenFilter jwtTokenFilter;
@Resource
private CorsFilter corsFilter;
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// 搜寻匿名标记 url: @AnonymousAccess
Map handlerMethodMap = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Set anonymousUrls = new HashSet<>();
for (Map.Entry infoEntry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = infoEntry.getValue();
AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
if (null != anonymousAccess) {
anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
}
}
httpSecurity
// 禁用 CSRF
.csrf().disable()
// 授权异常
.exceptionHandling()
.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler)
// 防止iframe 造成跨域
.and()
.headers()
.frameOptions()
.disable()
// 不创建会话
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 静态资源等等
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/**/*.map","/**/*.ttf","/**/*.woff","/**/*.woff2",
"/**/*.ico"
).permitAll()
// swagger 文档
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll()
// 文件
.antMatchers("/avatar/**").permitAll()
.antMatchers("/image/**").permitAll()
// 阿里巴巴 druid
.antMatchers("/druid/**").permitAll()
// 报表
.antMatchers("/ureport/**").permitAll()
// 放行OPTIONS请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 自定义匿名访问所有url放行 : 允许匿名和带权限以及登录用户访问
.antMatchers(anonymousUrls.toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated();
//跨域
httpSecurity.addFilterBefore(corsFilter,UsernamePasswordAuthenticationFilter.class);
//用于携带token的认证
httpSecurity.addFilterBefore(jwtTokenFilter,UsernamePasswordAuthenticationFilter.class);
}
}
? WebSecurityConfigurerAdapter
类是Spring提供的安全配置类的基础实现,通常情况我们都需要继承它,当然也可以自己实现WebSecurityConfigurer
接口来自定义一个实现。
? WebSecurityConfigurerAdapter
类是我们必须掌握的。
? 在配置的最后加入的jwtTokenFilter
是应用中自定义的过滤器,作用是JWT
令牌认证。也就是我们可以将自定义的过滤器嵌入到SpringSecurity
过滤链中,而不是加入到普通的Servlet Filter
链中。这个时候我们自己的过滤建议实现OncePerRequestFilter
接口,避免过滤器被普通过滤链加载而重复执行,或者不要将过滤器加入到IOC
容器中,而是使用如下new的方式:
httpSecurity.addFilterBefore(new JwtTokenFilter(),UsernamePasswordAuthenticationFilter.class);
2、Spring Security的过滤链
SpringSecurity
的过滤器长什么样子?
它在Servlet
过滤链中加入了代理,这个代理也是一个过滤器,但是其内部又实现一系列的过滤器。
原生过滤链:
0=characterEncodingFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter
1=formContentFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedFormContentFilter
2=requestContextFilter, filterClass=org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter
3=springSecurityFilterChain, filterClass=org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1
4=webStatFilter, filterClass=com.alibaba.druid.support.http.WebStatFilter
5=jwtTokenFilter, filterClass=com.wood.system.security.JwtTokenFilter
6=corsFilter, filterClass=org.springframework.web.filter.CorsFilter
7=Tomcat WebSocket (JSR356) Filter, filterClass=org.apache.tomcat.websocket.server.WsFilter
其中springSecurityFilterChain
是额外加入的过滤器,内部定义了Spring Security实现的过滤链:
0 = {WebAsyncManagerIntegrationFilter@12079}
1 = {SecurityContextPersistenceFilter@12080}
2 = {HeaderWriterFilter@12081}
3 = {LogoutFilter@12082}
4 = {CorsFilter@12083}
5 = {JwtTokenFilter@10680}
6 = {RequestCacheAwareFilter@12084}
7 = {SecurityContextHolderAwareRequestFilter@12085}
8 = {AnonymousAuthenticationFilter@12086}
9 = {SessionManagementFilter@12087}
10 = {ExceptionTranslationFilter@12088}
11 = {FilterSecurityInterceptor@12089}
3、过滤链里Spring Security过滤器的顺序
? 过滤器的顺序有严格的要求,它是通过FilterComparator
来实现的,代码片段如下:
FilterComparator() {
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
put(ChannelProcessingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(WebAsyncManagerIntegrationFilter.class, order.next());
put(SecurityContextPersistenceFilter.class, order.next());
put(HeaderWriterFilter.class, order.next());
put(CorsFilter.class, order.next());
put(CsrfFilter.class, order.next());
put(LogoutFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
order.next());
filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
order.next());
put(X509AuthenticationFilter.class, order.next());
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter",
order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
order.next());
filterToOrder.put(
"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
order.next());
put(UsernamePasswordAuthenticationFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
put(DefaultLoginPageGeneratingFilter.class, order.next());
put(DefaultLogoutPageGeneratingFilter.class, order.next());
put(ConcurrentSessionFilter.class, order.next());
put(DigestAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter", order.next());
put(BasicAuthenticationFilter.class, order.next());
put(RequestCacheAwareFilter.class, order.next());
put(SecurityContextHolderAwareRequestFilter.class, order.next());
put(JaasApiIntegrationFilter.class, order.next());
put(RememberMeAuthenticationFilter.class, order.next());
put(AnonymousAuthenticationFilter.class, order.next());
filterToOrder.put(
"org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
order.next());
put(SessionManagementFilter.class, order.next());
put(ExceptionTranslationFilter.class, order.next());
put(FilterSecurityInterceptor.class, order.next());
put(SwitchUserFilter.class, order.next());
}
FilterComparator是在HttpSecurity被初始化,并应用到各过滤器里。
4、SpringSecurity相关的过滤器是什么时候加入到过滤链中的?
? Spring Security相关的过滤器通过HttpSecurity
的AddFilter
方法加入到过滤链中,加入的时机是各种Security相关的Configurer初始化的时候。
? 例如:跨域的过滤器是在CorsConfigurer
里加入的。类似的配置类还有:AbstractInterceptUrlConfigurer
中方法拦截过滤器,ExceptionHandlingConfigurer
中的异常处理过滤器等。官方的SpringSecurity
过滤器有几个可以查看FilterComparator
。
有的特殊的过滤器WebAsyncManagerIntegrationFilter
是在WebSecurityConfigurerAdapter
里加入的。
? 这些Configurer
初始化是在HttpSecurity
中开始的:
public CsrfConfigurer csrf() throws Exception {
ApplicationContext context = getContext();
return getOrApply(new CsrfConfigurer<>(context));
}
private > C getOrApply(
C configurer) throws Exception {
C existingConfig = (C) getConfigurer(configurer.getClass());
if (existingConfig != null) {
return existingConfig;
}
return apply(configurer);
}
最终在AbstractConfiguredSecurityBuilder
中完成配置调用:
private void configure() throws Exception {
Collection> configurers = getConfigurers();
for (SecurityConfigurer configurer : configurers) {
configurer.configure((B) this);
}
}
private void init() throws Exception {
Collection> configurers = getConfigurers();
for (SecurityConfigurer configurer : configurers) {
configurer.init((B) this);
}
for (SecurityConfigurer configurer : configurersAddedInInitializing) {
configurer.init((B) this);
}
}
二、过滤器的入口--自动配置
? Spring Security过滤器的自动配置是所有基本配置的源头,这些关联配置包括:SecurityAutoConfiguration
和SecurityFilterAutoConfiguration
,后者依赖前者。
1、SecurityAutoConfiguration
? 作用主要完成Security相关的基础配置导入,基本bean的生成注入。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(DefaultAuthenticationEventPublisher.class)
@EnableConfigurationProperties(SecurityProperties.class)
@Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class,
SecurityDataConfiguration.class })
public class SecurityAutoConfiguration {
@Bean
@ConditionalOnMissingBean(AuthenticationEventPublisher.class)
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
主要内容:
1)导入SecurityProperties
类,此类中定义认证和授权相关过滤器的顺序和缺省用户和密码等,该类是配置类,可以在application.yml
通过spring.security
进行配置。
2)注入SpringBootWebSecurityConfiguration
,WebSecurityEnablerConfiguration
,SecurityDataConfiguration
三个配置。重点。
3)实例化DefaultAuthenticationEventPublisher
,缺省认证事件发布器,这个暂时不是重点。
1.1、SpringBootWebSecurityConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
public class SpringBootWebSecurityConfiguration {
@Configuration(proxyBeanMethods = false)
@Order(SecurityProperties.BASIC_AUTH_ORDER)
static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
}
}
主要内容:
1)确保WebSecurityConfigurerAdapter
类存在。
2)WebSecurityConfigurerAdapter
还没有实例化注入容器。
3)必须是一个web应用,并且类型是Servlet
。
4)重点:如果满足上述条件,注入WebSecurityConfigurerAdapter
类的bean作为缺省安全认证配置,并且重新指定其生效顺序为SecurityProperties.BASIC_AUTH_ORDER
,这个顺序值如下:
public static final int BASIC_AUTH_ORDER = Ordered.LOWEST_PRECEDENCE - 5;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
如果没有这个@Order(SecurityProperties.BASIC_AUTH_ORDER)
指定,缺省的WebSecurityConfigurerAdapter
顺序为100。
注意:这里的@Order是配置类的加载顺序,不是过滤器的加载顺序,他们表现一样,但是要实现的最终目的是不一样的。配置类中,先加载的bean,如果是单例(大部分),那么可能后续相同的配置bean就无法加载,并且还有类似@ConditionalOnMissingBean
这样的注解辅助,所以结论如下:
Spring Security缺省以很低的优先级(比它更低的优先级只有5个空位)加载了WebSecurityConfigurerAdapter
配置。本文开头所说,一般我们需要继承实现WebSecurityConfigurerAdapter
配置,那么我们自己应用缺省的加载顺序就是100,这样我们应用配置就优先加载,Spring Security的DefaultConfigurerAdapter
配置就无法加载。
1.2、WebSecurityEnablerConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(WebSecurityConfigurerAdapter.class)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableWebSecurity
public class WebSecurityEnablerConfiguration {
}
主要内容:
1)WebSecurityConfigurerAdapter
类bean必须注入IOC。
2)springSecurityFilterChain
过滤链的bean没有创建注入IOC容器。这个过滤链就是Spring Security的过滤链。
3)重点:@EnableWebSecurity
,加载默认Web自动安全配置。这个注解我们一般在继承实现WebSecurityConfigurerAdapter
类的时候都会加入,如果忘记加了,那么spring security初始化时就会自动加载缺省配置。
1.2.1 @EnableWebSecurity
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
SpringWebMvcImportSelector.class,
OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {
/**
* Controls debugging support for Spring Security. Default is false.
* @return if true, enables debug support with Spring Security
*/
boolean debug() default false;
}
主要内容:
1)加载WebSecurityConfiguration
。如下精简代码:
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
private WebSecurity webSecurity;
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public SecurityExpressionHandler webSecurityExpressionHandler() {
return webSecurity.getExpressionHandler();
}
@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public Filter springSecurityFilterChain() throws Exception {
...
return webSecurity.build();
}
@Bean
@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
return webSecurity.getPrivilegeEvaluator();
}
}
里面有重要的三个bean:
webSecurityExpressionHandler
安全表达式处理器
springSecurityFilterChain
Security过滤链,也就是整个Spring Security核心的过滤链bean在这里创建。
WebInvocationPrivilegeEvaluator
web方法调用评估器
2)加载SpringWebMvcImportSelector
。作用是加载DispatcherServlet类
,WebMvcSecurityConfiguration
类,这两个类不属于主线任务。
3)加载OAuth2ImportSelector
。作用是加载oAuth2、WebFlux相关的类,先略过。
4)加载@EnableGlobalAuthentication
。重点。
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}
主要导入AuthenticationConfiguration
类,这个配置类主要注入了AuthenticationManagerBuilder
,通过它,我们就可以拿到AuthenticationManager
,然后进行自动或者手动的认证。
1.3、SecurityDataConfiguration
这个是与Spring Data的安全集成,略过。
2、SecurityFilterAutoConfiguration
? Spring Security过滤链的配置与注册。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
@AutoConfigureAfter(SecurityAutoConfiguration.class)
public class SecurityFilterAutoConfiguration {
private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
@Bean
@ConditionalOnBean(name = DEFAULT_FILTER_NAME)
public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
SecurityProperties securityProperties) {
DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
DEFAULT_FILTER_NAME);
registration.setOrder(securityProperties.getFilter().getOrder());
registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
return registration;
}
... ...
}
主要内容:
1)导入SecurityProperties
配置
2)确保在SecurityAutoConfiguration
之后执行
3)注入DelegatingFilterProxyRegistrationBean
,它就是springSecurityFilterChain
过滤链的委托对象。在前面提到普通的Servlet filter过滤链中
3=springSecurityFilterChain, filterClass=org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1
springSecurityFilterChain
是以代理方式实现,这是为了spring security内部需要走一遍自己的过滤器,在我之前的应用里是11个过滤器。
4)DelegatingFilterProxyRegistrationBean
在生成的时候设置了过滤器的顺序为-100。
public static final int DEFAULT_FILTER_ORDER = OrderedFilter.REQUEST_WRAPPER_FILTER_MAX_ORDER - 100;
int REQUEST_WRAPPER_FILTER_MAX_ORDER = 0;
三、认证(Authentication)
1、认证接口
? 用于认证的主要接口是AuthenticationManager
,一般我们可以使用AuthenticationManagerBuilder
获取它的默认实现,它只有一个方法:
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
}
一个 AuthenticationManager
的authenticate()
方法中有三种情况:
- 返回
Authentication
(authenticated=true
),如果验证输入是合法的Principal
)。 - 抛出
AuthenticationException
异常,如果输入不合法。 - 如果无法判断,则返回
null
。
AuthenticationException
是一个运行时异常,通常被应用程序以常规的方式的处理,这取决于应用目的和代码风格。换句话说,代码中一般不会捕捉和处理这个异常。比如,可以使得网页显示认证失败,后端返回 401 HTTP 状态码,响应头中的WWW-Authenticate
有无视情况而定。
AuthenticationManager
最普遍的实现是ProviderManager
,ProviderManager
将认证委托给一系列的AuthenticationProvider
实例 。AuthenticationProvider
和 AuthenticationManager
很类似,但是它有一个额外的方法允许查询它支持的Authentication
方式:
public interface AuthenticationProvider {
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
boolean supports(Class<?> authentication);
}
supports
方法的Class authentication
参数其实是Class
类型的。一个ProviderManager
在一个应用中能支持多种不同的认证机制,通过将认证委托给一系列的AuthenticationProvider
。ProviderManager
没有识别出的认证类型,将会被忽略。
每个ProviderManager
可以有一个父类,如果所有AuthenticationProvider
都返回null
,那么就交给父类去认证。如果父类也不可用,则抛出AuthenticationException
异常。
有时应用的资源会有逻辑分组(比如所有网站资源都匹配URL/api/**
),并且每个组都有自己的AuthenticationManager
,通常是一个ProviderManager
,它们之间有共同的父类认证器。那么父类就是一种全局资源,充当所有认证器的 fallback。
2、自定义AuthenticationManager
Spring Security 提供了一些配置方式帮助你快速的配置通用的AuthenticationManager
。最常见的是AuthenticationManagerBuilder
,它可以使用内存方式(in-memory)、JDBC 或 LDAP、或自定义的UserDetailService
来认证用户。下面是设置全局认证器的例子:
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
... // web stuff here
@Autowired
public initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}
}
虽然这个例子仅仅设计一个 web 应用,但是AuthenticationManagerBuilder
的用处大为广阔(详细情况请看[Web 安全](#Web 安全)是如何实现的)。请注意AuthenticationManagerBuilder
是通过@AutoWired
注入到被@Bean
注解的一个方法中的,这使得它成为一个全局AuthenticationManager
。相反的,如果我们这样写:
@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
... // web stuff here
@Override
public configure(AuthenticationManagerBuilder builder) {
builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
.password("secret").roles("USER");
}
}
重写configure(AuthenticationManagerBuilder builder)
方法,那么AuthenticationManagerBuilder
仅会构造一个“本地”的AuthenticationManager
,只是全局认证器的一个子实现。在 Spring Boot 应用中你可以使用@Autowired
注入全局的AuthenticationManager
,但是你不能注入“本地”的,除非你自己公开暴露它。
Spring Boot 提供默认的全局AuthenticationManager
,除非你提供自己的全局AuthenticationManager
。不用担心,默认的已经足够安全了,除非你真的需要一个自定义的全局AuthenticationManager
。一般的,你只需只用“本地”的AuthenticationManagerBuilder
来配置,而不需要担心全局的。
3、认证与过滤器
? 默认情况下,Spring Security是通过UsernamePasswordAuthenticationFilter
的attemptAuthentication
方法实现认证的。它继承自AbstractAuthenticationProcessingFilter
,这个过滤器在AbstractAuthenticationFilterConfigurer
配置类中加入到过滤链中。在attemptAuthentication
方法中通过AuthenticationManager
接口实现进行认证。
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
? 在实际开发中,特别是前后端分离的项目中,大部分时候我们都需要自定义认证,例如:前后端分离+Jwt令牌认证改造,我们需要做三件事:
1)实现UserDetailsService
接口,重写loadUserByUsername
业务逻辑,重新封装返回的对象。
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Resource
private SysUserService userService;
@Override
public JwtUserDto loadUserByUsername(String username) {
SysUser sysUser = userService.queryByUserName(username);
UserDto userDto = new UserDto(sysUser);
userDto.setAvatar(avatar);
return new JwtUserDto(
userDto,
userService.getGrantedAuthoritiesByUserId(sysUser)
);
}
}
JwtUserDto
是UserDetails
的自定义扩展。
2)实现一个自定义的过滤器JwtTokenFilter
,建议实现OncePerRequestFilter
接口,在doFilterInternal
方法里实现对令牌的认证,如果有合法令牌,则设置SecurityContext
,然后继续走过滤链。
SecurityContextHolder.getContext().setAuthentication(authentication);
将过滤器加入到springSecurityFilterChain
中
httpSecurity.addFilterBefore(jwtTokenFilter,UsernamePasswordAuthenticationFilter.class);
注意:需要将WebSecurityConfigurerAdapter
的实现中去掉formLogin()
认证方式。这个时候过滤链中UsernamePasswordAuthenticationFilter
过滤器将被删除。
3)手动调用认证,并手动设置SecurityContext
,并生成令牌返回前端,下次前端访问带着令牌就会进入第二步的JwtTokenFilter
进行令牌的认证和安全上下文的设置。精简代码如下:
@AnonymousAccess
@PostMapping(value = "/login")
public Result