也该来学习微服务网关与用户身份识别,SpringSecurity原理和实战

1496 篇文章 10 订阅
1494 篇文章 14 订阅

Spring Security原理和实战

Web服务提供者的安全访问无疑是十分重要的,而SpringSecurity安全模块是保护Web应用的一个非常好的选择。

Spring Security是Spring应用项目中的一个安全模块,特别是在Spring Boot项目中,Spring Security默认为自动开启,可见其重要性。

在微服务架构下,建议仅将Spring Security组件应用于网关(如Zuul),对于集群内部的微服务提供者,不建议启用Spring Security组件,因为重复的验证会降低请求处理的性能。本文配套的crazyspringcloud微服务脚手架就是这样做的。

如果需要为微服务提供者关闭Spring Security组件的自动启动,那么可以在启动类上添加以下注解:

@EnableEurekaClient
@SpringBootApplication(scanBasePackages = {
 ...
}, exclude = {SecurityAutoConfiguration.class})

或者可以在应用配置文件中将它的自动配置类排除,具体代码如下:

spring:
 autoconfigure:
 exclude: org.springframework.boot.autoconfigure.security.servlet.
SecurityAutoConfiguration

6.4.1 Spring Security核心组件

Spring Security核心组件有Authentication(认证/身份验证)、AuthenticationProvider(认证提供者)、AuthenticationManager(认证管理者)等。下面分别介绍。

1.Spring Security核心组件之Authentication

Authentication直译是“认证”的意思,在Spring Security中,Authentication接口用来表示凭证或者令牌,可以理解为用户的用户名、密码、权限等信息。Authentication的代码如下:

public interface Authentication extends Principal, Serializable {
 //权限集合
 //可使用AuthorityUtils.commaSeparatedStringToAuthorityList
("admin, ROLE_ADMIN")进行初始化
 Collection<? extends GrantedAuthority> getAuthorities();
 //用户名和密码认证时,可以理解为密码
 Object getCredentials();
 //认证时包含的一些详细信息,可以是一个包含用户信息的POJO实例
 Object getDetails();
 //用户名和密码认证时,可以理解为用户名
 Object getPrincipal();
 //是否认证通过,通过为true
 boolean isAuthenticated();
 //设置是否认证通过
 void setAuthenticated(boolean isAuthenticated)
throws IllegalArgumentException;
}

下面对Authentication的方法进行说明,具体如下:

(1)getPrincipal方法:Principal直译为“主要演员、主角”,用于获取用户身份信息,可以是用户名,也可以是用户的ID等,具体的值需要依据具体的认证令牌实现类确定。

(2)getAuthorities方法:用于获取用户权限集合,一般情况下获取到的是用户的权限信息。

(3)getCredentials方法:直译为获取资格证书。用户名和密码认证时,通常情况下获取到的是密码信息。

(4)getDetails方法:用于获取用户的详细信息。用户名和密码认证时,这部分信息可以是用户的POJO实例。

(5)isAuthenticated方法:判断当前Authentication凭证是否已验证通过。

(6)setAuthenticated方法:设置当前Authentication凭证是否已验证通过(true或false)。

在Spring Security中,Authentication认证接口有很多内置的实现类,下面举例说明。

(1)
UsernamePasswordAuthenticationToken:用于在用户名+密码认证的场景中作为验证的凭证,该凭证(令牌)包含用户名+密码信息。

(2)
RememberMeAuthenticationToken:用于“记住我”的身份认证场景。如果用户名+密码成功认证之后,在一定时间内不需要再输入用户名和密码进行身份认证,就可以使用RememberMeAuthenticationToken凭证。通常是通过服务端发送一个Cookie给客户端浏览器,下次浏览器再访问服务端时,服务端能够自动检测客户端的Cookie,根据Cookie值自动触发RememberMeAuthenticationToken凭证/令牌的认证操作。

(3)
AnonymousAuthenticationToken:对于匿名访问的用户,Spring Security支持为其建立一个AnonymousAuthenticationToken匿名凭证实例存放在SecurityContextHolder中。

除了以上内置凭证类外,还可以通过实现Authentication定制自己的身份认证实现类。

2.Spring Security核心组件之AuthenticationProvider

AuthenticationProvider是一个接口,包含两个函数authenticate和supports,用于完成对凭证进行身份认证操作。

public interface AuthenticationProvider {
 //对实参authentication进行身份认证操作
 Authentication authenticate(Authentication authentication)
 throws AuthenticationException;
 //判断是否支持该authentication
 boolean supports(Class<?> authentication);
}

AuthenticationProvider接口的两个方法说明如下:

(1)authenticate方法:表示认证的操作,对authentication参数对象进行身份认证操作。如果认证通过,就返回一个认证通过的凭证/令牌。通过源码中的注释可以知道,如果认证失败,就抛出异常。

(2)supports方法:判断实参authentication是否为当前认证提供者所能认证的令牌。

在Spring Security中,AuthenticationProvider接口有很多内置的实现类,下面举例说明。

(1)
AbstractUserDetailsAuthenticationProvider:这是一个对UsernamePasswordAuthentication Token类型的凭证/令牌进行验证的认证提供者类,用于“用户名+密码”验证的场景。

(2)
RememberMeAuthenticationProvider:这是一个对


RememberMeAuthenticationToken类型的凭证/令牌进行验证的认证提供者类,用于

“记住我”的身份认证场景。

(3)
AnonymousAuthenticationProvider:这是一个对AnonymousAuthenticationToken类型的凭证/令牌进行验证的认证提供者类,用于匿名身份认证场景。

此外,如果自定义了凭证/令牌,并且Spring Security的默认认证提供者类不支持该凭证/令牌,就可以通过实现AuthenticationProvider接口来扩展出自定义的认证提供者。

3.Spring Security核心组件之AuthenticationManager

AuthenticationManager是一个接口,其唯一的authenticate验证方法是认证流

程的入口,接收一个Authentication令牌对象作为参数。

public interface AuthenticationManager {
 //认证流程的入口
 Authentication authenticate(Authentication authentication)
 throws AuthenticationException;
}AuthenticationManager的一个实现类名为ProviderManager,该类有一个
providers成员变量,负责管理一个提供者清单列表,其源码如下:
public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
...
//提供者清单
 private List<AuthenticationProvider> providers = Collections.emptyList();
//迭代提供者清单,找出支持令牌的提供者,交给提供者去执行令牌验证
 public Authentication authenticate(Authentication authentication)
 throws AuthenticationException {
 ...
 }
}

认证管理者ProviderManager在进行令牌验证时,会对提供者列表进行迭代,找出支持令牌的认证提供者,并交给认证提供者去执行令牌验证。如果该认证提供者的supports方法返回true,就会调用该提供者的authenticate方法。如果验证成功,那么整个认证过程结束;如果不成功,那么继续处理列表中的下一个提供者。只要有一个验证成功,就会认证成功。

6.4.2 Spring Security的请求认证处理流程

一个基础、简单的Spring Security请求认证的处理流程大致包括以下步骤:

(1)定制一个凭证/令牌类。

(2)定制一个认证提供者类和凭证/令牌类进行配套,并完成对自制凭证/令牌实例的验证。

(3)定制一个过滤器类,从请求中获取用户信息组装成定制凭证/令牌,交给认证管理者。

(4)定制一个HTTP的安全认证配置类(AbstractHttpConfigurer子类),将上一步定制的过滤器加入请求的过滤处理责任链。

(5)定义一个Spring Security安全配置类(
WebSecurityConfigurerAdapter子类),对Web容器的HTTP安全认证机制进行配置。

为了演示,这里实现一个非常简单的认证处理流程,具体的功能如下:

当系统资源被访问时,过滤器从HTTP的token请求头获取用户名和密码,然后与系统中的用户信息进行匹配,如果匹配成功,就可以访问系统资源,否则返回403响应码,表示未授权。演示程序的代码位于本书配套源码的demo-provider模块中。

演示程序的第一步:定制一个凭证/令牌类,封装用户的用户名和密码。所定制的DemoToken令牌的代码如下:

package com.crazymaker.springcloud.demo.security;
//省略import
public class DemoToken extends AbstractAuthenticationToken
{
 //用户名称
 private String userName;
 //密码
 private String password;
 ...
}

演示程序的第二步:定制一个认证提供者类和凭证/令牌类进行配套,并完成对自制凭证/令牌实例的验证。所定制的DemoAuthProvider类的代码如下:

public class DemoAuthProvider implements AuthenticationProvider
{
 public DemoAuthProvider()
 {
 }
 //模拟的数据源,实际场景从DB中获取
 private Map<String, String> map = new LinkedHashMap<>();
 //初始化模拟的数据源,放入两个用户
 {
 map.put("zhangsan", "123456" );
 map.put("lisi", "123456" );
 }
 //具体的验证令牌方法
 @Override
 public Authentication authenticate(Authentication authentication) throws AuthenticationException
 {
 DemoToken token = (DemoToken) authentication;
 //从数据源map中获取用户密码
 String rawPass = map.get(token.getUserName());
 //验证密码,如果不相等,就抛出异常
 if (!token.getPassword().equals(rawPass))
 {
 token.setAuthenticated(false);
 throw new BadCredentialsException("认证有误:令牌校验失败" );
 }
 //验证成功
 token.setAuthenticated(true);
 return token;
 }
 /**
 *判断令牌是否被支持
 *@param authentication 这里仅仅DemoToken令牌被支持
 *@return
 */
 @Override
 public boolean supports(Class<?> authentication)
 {
 return authentication.isAssignableFrom(DemoToken.class);
 }
}

DemoAuthProvider模拟了一个简单的数据源并且加载了两个用户。在其authenticate验证方法中,将入参DemoToken令牌中的用户名和密码与模拟数据源中的用户信息进行匹配,若匹配成功,则验证成功。

演示程序的第三步:定制一个过滤器类,从请求中获取用户信息并组装成定制凭证/令牌,交给认证管理者。在生产场景中,认证信息一般为某个HTTP头部信息(如Cookie信息、Token信息等)。本演示程序中的过滤器类为DemoAuthFilter,从请求头中获取token字段,解析之后组装成DemoToken令牌实例,提交给AuthenticationManager进行验证。DemoAuthFilter的代码如下:

public class DemoAuthFilter extends OncePerRequestFilter
{
 //认证失败的处理器
 private AuthenticationFailureHandler failureHandler = new AuthFailureHandler();
 ...
 //authenticationManager是认证流程的入口,接收一个Authentication令牌对象作为参数 private AuthenticationManager authenticationManager;
 @Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
 {
 ...
 AuthenticationException failed = null;
 try
 {
 Authentication returnToken=null;
 boolean succeed=false;
 //从请求头中获取认证信息
 String token = request.getHeader
(SessionConstants.AUTHORIZATION_HEAD);
 String[] parts = token.split("," );
 //组装令牌
 DemoToken demoToken = new DemoToken(parts[0],parts[1]);
 //提交给AuthenticationManager进行令牌验证
 returnToken = (DemoToken) this.getAuthenticationManager()
.authenticate(demoToken);
 //获取认证成功标志
 succeed=demoToken.isAuthenticated();
 if (succeed)
 {
 //认证成功,设置上下文令牌
 SecurityContextHolder.getContext().setAuthentication
(returnToken);
 //执行后续的操作
 filterChain.doFilter(request, response);
 return;
 }
 } catch (Exception e)
 {
 logger.error("认证有误", e);
 failed = new AuthenticationServiceException("请求头认证消息格式错误",e );
 }
 if(failed == null)
 {
 failed = new AuthenticationServiceException("认证失败");
 }
 //认证失败了
 SecurityContextHolder.clearContext();
 failureHandler.onAuthenticationFailure(request, response, failed);
 }
 ...
}

为了使得过滤器能够生效,必须将过滤器加入Web容器的HTTP过滤处理责任链,此项工作可以通过实现一个AbstractHttpConfigurer配置类来完成。

演示程序的第四步:定制一个HTTP的安全认证配置类(AbstractHttpConfigurer子类),将上一步定制的过滤器加入请求的过滤处理责任链。定制的DemoAuthConfigurer代码如下:

public class DemoAuthConfigurer<T extends DemoAuthConfigurer<T, B>, B
 extends HttpSecurityBuilder<B>> extends AbstractHttpConfigurer<T, B>
{
 //创建认证过滤器
 private DemoAuthFilter authFilter = new DemoAuthFilter();
 //将过滤器加入http过滤处理责任链
 @Override
 public void configure(B http) throws Exception
 {
 //获取Spring Security共享的AuthenticationManager认证管理者实例
将其设置到认证过滤器 //将其设置到认证过滤器
 authFilter.setAuthenticationManager(http.getSharedObject
(AuthenticationManager.class));
 DemoAuthFilter filter = postProcess(authFilter);
 //将过滤器加入http过滤处理责任链
 http.addFilterBefore(filter, LogoutFilter.class);
 }
}

演示程序的第五步:定义一个Spring Security安全配置类(
WebSecurityConfigurerAdapter子类),对Web容器的HTTP安全认证机制进行配置。这一步有两项工作:一是应用DemoAuthConfigurer配置类;二是构造AuthenticationManagerBuilder认证管理者实例。定制类DemoWebSecurityConfig的代码如下:

@EnableWebSecurity
public class DemoWebSecurityConfig extends WebSecurityConfigurerAdapter
{
 //配置HTTP请求的安全策略,应用DemoAuthConfigurer配置类实例
 protected void configure(HttpSecurity http) throws Exception
 {
 http.csrf().disable()
...
 .and()
 //应用DemoAuthConfigurer配置类
 .apply(new DemoAuthConfigurer<>())
 .and()
 .sessionManagement().disable();
 }
 //配置认证Builder,由其负责构造AuthenticationManager认证管理者实例
 //Builder将构造AuthenticationManager实例,并且作为HTTP请求的共享对象存储
 //在代码中可以通过http.getSharedObject(AuthenticationManager.class) 来获取管理者实例
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception
 {
 //加入自定义的Provider认证提供者实例
 auth.authenticationProvider(demoAuthProvider());
 }
 //自定义的认证提供者实例
 @Bean("demoAuthProvider" )
 protected DemoAuthProvider demoAuthProvider()
 {
 return new DemoAuthProvider();
 }
}

如何对以上自定义的安全认证机制进行验证呢?首先启动demo-provider服务,然后在浏览器中访问其swagge-ui界面,如图6-4所示。

也该来学习微服务网关与用户身份识别,SpringSecurity原理和实战

图6-4 demo-provider服务的swagge-ui界面

然后在swagger-ui界面访问/api/demo/hello/v1,发现认证失败,如图6-5所示。

也该来学习微服务网关与用户身份识别,SpringSecurity原理和实战

图6-5 直接访问/api/demo/hello/v1返回认证失败

这是由于前面所定义的Spring Security的请求认证处理流程已经生效。接下来在swagger-ui界面再一次访问/api/demo/hello/v1,不过这一次给token请求头输入了正确的用户名和密码,如图6-6所示。

也该来学习微服务网关与用户身份识别,SpringSecurity原理和实战

图6-6 给token请求头输入了正确的用户名和密码

最后,再一次访问/api/demo/hello/v1,发现请求的返回值已经正常,表明前面所定义的Spring Security的请求认证处理流程起到了对请求进行用户名和密码验证的作用。

6.4.3 基于数据源的认证流程

在大多数生产场景中,用户信息都存储在某个数据源(如数据库)中,认证过程中涉及从数据源加载用户信息的环节。Spring Security为这种场景内置了一套解决方案,主要涉及几个内置类。

1.UsernamePasswordAuthenticationToken

此认证类实现了Authentication接口,主要封装用户输入的用户名和密码信息,提供给支持的认证提供者进行认证。

2.AbstractUserDetailsAuthenticationProvider

此认证提供者类与
UsernamePasswordAuthenticationToken凭证/令牌类配套,但这是一个抽象类,具体的验证逻辑需要由子类完成。

此认证提供者类的常用子类为DaoAuthenticationProvider类,该类依赖一个UserDetailsService用户服务数据源,用于获取UserDetails用户信息,其中包括用户名、密码和所拥有的权限等。此认证提供者子类从数据源UserDetailsService中加载用户信息后,将待认证的令牌中的“用户名+密码”信息和所加载的数据源用户信息进行匹配和验证。

3.UserDetailsService

UserDetailsService有一个loadUserByUsername方法,其作用是根据用户名从数据源中查询用户实体。一般情况下,可以实现一个定制的UserDetailsService接口的实现类来从特定的数据源获取用户信息。用户信息服务接口的源码如下:

public interface UserDetailsService {
 //通过用户名从数据源加载用户信息
 UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

4.UserDetails

UserDetails是一个接口,主要封装用户名、密码、是否过期、是否可用等信息。此接口的源码如下:

 public interface UserDetails extends Serializable {
 //权限集合
 Collection<? extends GrantedAuthority> getAuthorities();
 //密码,一般为密文
 String getPassword();
 //用户名
 String getUsername();
 //用户名是否未过期
 boolean isAccountNonExpired();
 //用户名是否未锁定
 boolean isAccountNonLocked();
 //用户密码是否未过期
 boolean isCredentialsNonExpired();
 //账号是否可用(可理解为是否删除)
 boolean isEnabled();
 }

UserDetails接口的密码属性和
UsernamePasswordAuthenticationToken的密码属性的区别在于:前者的密码来自数据源,是密文;后者的密码来自用户请求,是明文。明文和密文的匹配工作由PasswordEncoder加密器完成。

5.PasswordEncoder

PasswordEncoder是一个负责明文加密、判断明文和密文匹配的接口,源码如下:

public interface PasswordEncoder {
 //对明文rawPassword加密
 String encode(CharSequence rawPassword);
 //判断rawPassword与encodedPassword是否匹配
 boolean matches(CharSequence rawPassword, String encodedPassword);
}

DaoAuthenticationProvider提供者在验证之前会通过内部的PasswordEncoder加密器实例对令牌中的密码明文和UserDetails中的密码密文进行匹配。若匹配不成功,则令牌验证不通过。

PasswordEncoder的内置实现类有多个,如BCryptPasswordEncoder、Pbkdf2PasswordEncoder等。其中BCryptPasswordEncoder比较常用,其采用SHA-256+密钥+盐的组合方式对密码明文进行Hash编码处理。注意,SHA-256是Hash编码算法,不是加密算法。这里是对明文编码而不是加密,这是因为加密算法往往可以解密,只是解密的复杂度不同;而编码算法则不一样,其过程是不可逆的。

密码明文编码之后,只有用户知道密码,甚至后台管理员都无法直接看到用户的密码明文。当用户忘记密码后,只能重置密码(通过手机验证码或者邮箱的形式)。所以,即使数据库泄露,黑客也很难破解密码。

推荐使用BCryptPasswordEncoder来进行密码明文的编码,本文配套的微服务脚手架中通过配置类配置了一个全局的加密器IOC容器实例,参考代码如下:

package com.crazymaker.springcloud.standard.config;
//省略import
/**
 *密码加密器配置类
 */
@Configuration
public class DefaultPasswordConfig
{
 /**
 *装配一个全局的Bean,用于密码加密和匹配
 *
 *@return BCryptPasswordEncoder加密器实例
 */
 @Bean
 public PasswordEncoder passwordEncoder()
 {
 return new BCryptPasswordEncoder();
 }
}

此类处于脚手架的base-runtime模块中,默认已经完成了Bean的装配,其他的模块只要直接通过@Resource注解装配即可。

作为基于数据源的认证流程演示程序,这里简单改造6.4.2节的实例,使用基于数据源的请求认证方式完成认证处理,并且依据6.4.2节中认证流程的5个步骤进行说明。

演示程序的第一步:定制一个凭证/令牌类。本演示程序直接使用Spring Security提供的
UsernamePasswordAuthenticationToken认证类存放用户名+密码信息,故这里不再定制自己的凭证/令牌类。

演示程序的第二步:定制一个认证提供者类和凭证/令牌类进行配套。

本演示程序直接使用Spring Security提供的提供者实现类DaoAuthenticationProvider,并在项目的Spring Security的启动配置类(本演示程序中为DemoWebSecurityConfig类)中创建该提供者的Bean实例。需要注意的是,该提供者有两个依赖:一个是UserDetailsService类型的用户信息服务实例;另一个是PasswordEncoder类型的加密器实例。

在项目的启动配置类中装配DaoAuthenticationProvider提供者容器实例的参考代码如下:

package com.crazymaker.springcloud.demo.config;
...
@EnableWebSecurity
public class DemoWebSecurityConfig extends WebSecurityConfigurerAdapter
{
 ...
 //注入全局BCryptPasswordEncoder加密器容器实例
 @Resource
 private PasswordEncoder passwordEncoder;
 //注入数据源服务容器实例
 @Resource
 private DemoAuthUserService demoUserAuthService;
 @Bean("daoAuthenticationProvider")
 protected AuthenticationProvider daoAuthenticationProvider() throws Exception
 {
 //创建一个数据源提供者
 DaoAuthenticationProvider daoProvider = new DaoAuthenticationProvider();
 //设置加密器
 daoProvider.setPasswordEncoder(passwordEncoder);
 //设置用户数据源服务
 daoProvider.setUserDetailsService(demoUserAuthService);
 return daoProvider;
 }
}

代码中所依赖的PasswordEncoder类的加密器IOC实例会注入base-runtime模块所装配的全局BCryptPasswordEncoder类的passwordEncoder Bean。代码中所依赖的数据源服务IOC实例的类是一个自定义的数据源服务类,名为DemoAuthUserService,核心代码如下:

package com.crazymaker.springcloud.demo.security;
//省略import
@Slf4j
@Service
public class DemoAuthUserService implements UserDetailsService
{
 //模拟的数据源,实际从DB中获取
 private Map<String, String> map = new LinkedHashMap<>();
 //初始化模拟的数据源,放入两个用户
 {
 map.put("zhangsan", "123456");
 map.put("lisi", "123456");
}
 /**
 *装载系统配置的加密器
 */
 @Resource
 private PasswordEncoder passwordEncoder;
 public UserDetails loadUserByUsername(String username)
 throws UsernameNotFoundException
 {
 //实际场景中需要从数据库加载用户
 //这里出于演示的目的,用map模拟真实的数据源
 String password = map.get(username);
 if (password == null)
 {
 return null;
 }
 if (null == passwordEncoder)
 {
 passwordEncoder = CustomAppContext.getBean
(PasswordEncoder.class);
 }
 /**
 *返回一个用户详细实例,包含用户名、加密后的密码、用户权限清单、用户角色
 */
 UserDetails userDetails = User.builder()
 .username(username)
 .password(passwordEncoder.encode(password))
 .authorities(SessionConstants.USER_INFO)
 .roles("USER")
 .build();
 return userDetails;
 }
}

Spring Security的DaoAuthenticationProvider在验证令牌时,会将令牌中的密码明文和用户详细实例UserDetails中的密码密文通过其内部的PasswordEncoder加密器实例进行匹配。所以,UserDetails中的密文在加密时用的加密器和DaoAuthenticationProvider中的认证加密器是同一种类型,需要使用同样的编码/加密算法,以保证能匹配成功。本演示程序中,由于二者使用的都是全局加密器IOC容器实例,因此加密器的类型和算法自然是一致的。

演示程序的第三步:定制一个过滤器类,从请求中获取用户信息组装成定制凭证/令牌,交给认证管理者。这一步使用6.4.2节的DemoAuthFilter过滤器,仅进行简单的修改:从请求中获取token头部字段,解析之后组装成UserDetails,然后构造一个“用户名+密码”类型的
UsernamePasswordAuthenticationToken令牌实例,提交给AuthenticationManager进行验证。

package com.crazymaker.springcloud.demo.security;
//省略import
public class DemoAuthFilter extends OncePerRequestFilter
{
 ...
 @Override
 protected void doFilterInternal(HttpServletRequest request,
 HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException
 {
 ...
 try
 {
 Authentication returnToken=null;
 boolean succeed=false;
 String token = request.getHeader(SessionConstants
.AUTHORIZATION_HEAD);
 String[] parts = token.split("," );
 //方式二:数据源认证演示
 UserDetails userDetails = User.builder()
 .username(parts[0])
 .password(parts[1])
 .authorities(SessionConstants.USER_INFO)
 .build();
 //创建一个用户名+密码的凭证,一般情况下,令牌中的密码需要明文
 Authentication userPassToken = new UsernamePasswordAuthenticationToken(userDetails,
 userDetails.getPassword(),
 userDetails.getAuthorities());
 //进入认证流程
 returnToken =this.getAuthenticationManager()
.authenticate(userPassToken);
 succeed=userPassToken.isAuthenticated();
 if (succeed)
 {
 //认证成功,设置上下文令牌
 SecurityContextHolder.getContext()
.setAuthentication(returnToken);
 //执行后续的操作
 filterChain.doFilter(request, response);
 return;
 }
 } catch (Exception e)
 {
 logger.error("认证有误", e);
 failed = new AuthenticationServiceException("请求头认证消息格式错误",e );
 }
 ...
 }
 ...
}

以上过滤器实现代码除了认证的令牌不同之外,其他的代码和6.4.2节基本是一致的。

演示程序的第四步:定制一个HTTP的安全认证配置类(AbstractHttpConfigurer子类),将上一步定制的过滤器加入请求的过滤处理责任链。

演示程序的第五步:定义一个Spring Security安全配置类(
WebSecurityConfigurerAdapter子类),对Web容器的HTTP的安全认证机制进行配置。

第四步、第五步的实现代码和6.4.2节中第四步、第五步的实现代码是完全一致的,这里不再赘述。

完成以上五步后,一个基于数据源的认证流程就完成了。重启项目后,可以参考6.4.2节的自验证方法进行Spring Security的认证拦截验证。

本文给大家讲解的内容是 微服务网关与用户身份识别,Spring Security原理和实战

  1. 下篇文章给大家讲解的是微服务网关与用户身份识别,JWT+Spring Security进行网关安全认证;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值