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

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的认证拦截验证。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 服务鉴权中心是指在服务架构中,使用一个独立的鉴权中心来统一管理和验证各个服务的访问权限。而网是作为服务鉴权中心的入口,负责对外提供服务,同时也是服务的访问控制和认证的第一层防线。在网配置中使用Spring Security OAuth2是一种常见的实现方式。 首先,需要在网项目中引入Spring Security OAuth2的依赖。可以使用Maven或Gradle将所需的依赖加入到项目的配置文件中。 接下来,需要配置Spring Security OAuth2的相信息,包括鉴权中心的认证服务器地址、客户端ID和密钥等。这些配置可以在网项目的配置文件中设置。 然后,需要配置网的请求过滤器,用于拦截并验证请求。可以实现一个自定义的过滤器,继承自Spring Security的AbstractAuthenticationProcessingFilter类,并覆写其中的相方法。 在过滤器中,需要使用OAuth2的认证流程来验证请求的合法性。可以使用RestTemplate发送请求到鉴权中心的认证服务器,获取访问令牌和授权信息。 接着,可以通过OAuth2AuthenticationManager来管理认证信息,并创建并设置Spring Security的Authentication对象。可以使用自定义的处理器将认证信息传递给后续的服务。 最后,需要对网的接口进行权限的配置。可以使用Spring Security的注解来定义接口的访问权限,如@PreAuthorize和@Secured。 通过以上的配置,网就可以实现对请求的鉴权和认证了。对于未经授权的请求,网会拒绝访问,并返回相应的错误信息。对于经过鉴权验证的请求,网会将请求转发给相应的服务进行处理。同时,网还可以对返回的结果进行处理和加工,以满足特定的需求。 总之,配置Spring Security OAuth2可以有效地实现服务鉴权中心之网的权限管理和访问控制,提高系统的安全性和可维护性。 ### 回答2: 服务鉴权中心作为一个独立的服务单元,负责管理和维护整个服务体系的权限和鉴权机制。网服务系统的入口,负责接收和处理所有外部请求。 在配置Spring Security OAuth2时,我们可以将鉴权中心和网进行整合,以实现统一的认证和授权机制。在网配置文件中,我们首先需要引入Spring Security OAuth2的依赖,然后定义一些必要的配置项。 首先,我们需要配置认证服务器的地址,通常是鉴权中心的地址。也就是说,所有的认证和授权请求都会转发到该地址进行处理。配置项如下: ``` security: oauth2: client: accessTokenUri: <鉴权中心地址>/oauth/token userAuthorizationUri: <鉴权中心地址>/oauth/authorize clientId: <客户端ID> clientSecret: <客户端密钥> ``` 其中,`accessTokenUri`表示获取访问令牌的地址,`userAuthorizationUri`表示用户授权的地址,`clientId`和`clientSecret`是鉴权中心针对网颁发的客户端ID和密钥。 接下来,我们需要配置资源服务器的地址,即服务的地址。配置项如下: ``` security: oauth2: resource: userInfoUri: <服务地址>/user ``` 其中,`userInfoUri`表示获取用户信息的地址。 最后,我们需要配置路由规则,指定哪些请求需要进行认证和授权。配置项如下: ``` spring: cloud: gateway: routes: - id: <路由ID> uri: <目标URI> predicates: - Path=/api/** filters: - TokenRelay metadata: authorization-uri: <鉴权中心地址>/oauth/authorize ``` 其中,`id`表示路由的唯一标识,`uri`表示目标URI,`predicates`指定路由的条件,`filters`指定过滤器,`metadata`指定认证和授权的地址。 通过以上配置,我们可以实现网和鉴权中心的整合,实现服务系统的统一认证和授权机制。网会将认证和授权请求转发到鉴权中心进行处理,并根据鉴权中心返回的结果进行相应的操作。 ### 回答3: 在服务架构中,鉴权是一个非常重要的部分,它能够确保只有经过授权的用户才能访问特定的服务。而网作为整个系统的入口,负责转发和路由请求,需要配置Spring Security和OAuth2来实现鉴权。 首先,需要在网服务中添加Spring Security和OAuth2的相依赖,并在配置文件中开启相功能。接着,需要创建一个自定义的鉴权过滤器,该过滤器会在请求到达网之后进行鉴权操作。 在过滤器中,首先需要配置一个OAuth2的资源服务。这个资源服务可以通过配置一个TokenStore来获取和保存令牌信息。然后,可以配置一个JwtAccessTokenConverter来验证和解析令牌。同时,需要配置一个ResourceServerTokenServices,该服务会验证令牌的正确性,是否过期等信息。 接下来,在过滤器中需要配置spring security的认证管理器,该管理器会根据请求头中的令牌信息来进行用户的鉴权操作。可以使用一个自定义的UserDetailsService来获取用户的权限信息,并将权限信息添加到SecurityContext中,以便后续的鉴权操作。 最后,在过滤器中,可以配置一些具体的鉴权规则,比如某些URL需要特定的角色才能访问,可以用.antMatchers().hasRole()的方式进行配置。 通过以上步骤,就可以实现网的鉴权功能。当用户发起请求时,网会根据请求头中的令牌信息进行鉴权,只有经过授权的用户才能访问特定的服务。这样可以确保整体系统的安全性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值