JWT+SpringSecurity进行网关安全认证

14 篇文章 5 订阅
4 篇文章 0 订阅

1 SpringSecurity

SpringSecurity进行网关安全认证的过程很简单,认证的过程就好比一个员工通过人脸,指纹或工牌等凭证进入公司。这里人脸,指纹,工牌就相当于一个凭证,其中任一凭证经过认证后都可以进入公司。经过以上分析不难猜出SpringSecurity主要包括三个组件:

  • Authentication(认证/身份验证)类比 人脸
  • AuthenticationProvider(认证提供者)类比 人脸识别应用
  • AuthenticationManager(认证管理者)类比 门禁系统(包含多张认证方式)
    下面分别介绍各个组件

1.1 SpringSecurity核心组件

1.1.1 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 IllegalArgum
    }

1.1.2 AuthenticationProvider

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

  public interface AuthenticationProvider {
        //对实参authentication进行身份认证操作 
        Authentication authenticate(Authentication authentication) throws AuthenticationException;

        // 判断是否支持该authentication 
        boolean supports(Class<?> authentication);
    }

1.1.3 AuthenticationManager

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

1.2 SpringSecurity的请求认证处理流程

(1)定制一个凭证/令牌类。
(2)定制一个认证提供者类和凭证/令牌类进行配套,并完成对自制凭证/令牌实例的验证。
(3)定制一个过滤器类,从请求中获取用户信息组装成定制凭证/令牌,交给认证管理者。
(4)定制一个HTTP的安全认证配置类(AbstractHttpConfigurer子类),将上一步定制的过滤器加入请求的过滤处理责任链。
(5)定义一个Spring Security安全配置类(WebSecurityConfigurerAdapter子类),对Web容器的HTTP安全认证机制进行配置。
为了演示,这里实现一个非常简单的认证处理流程,具体的功能如下:当系统资源被访问时,过滤器从HTTP的token请求头获取用户名和密码,然后与系统中的用户信息进行匹配,如果匹配成功,就可以访问系统资源,否则返回403响应码,表示未授权。演示程序的代码位于本书配套源码的demo-provider模块中。
第一步:定制一个凭证/令牌类。
本演示程序直接使用Spring Security提供的UsernamePasswordAuthenticationToken认证类存放用户名+密码信息。
第二步:定制一个认证提供者类和凭证/令牌类进行配套。
直接使用Spring Security提供的提供者实现类DaoAuthenticationProvider,并在项目的Spring Security的启动配置类(本演示程序中为DemoWebSecurityConfig类)中创建该提供者的Bean实例。需要注意的是,该提供者有两个依赖:一个是UserDetailsService类型的用户信息服务实例;另一个是PasswordEncoder类型的加密器实例。在项目的启动配置类中装配DaoAuthenticationProvider提供者容器实例的参考代码如下:

@EnableWebSecuritypublic
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;
    }
}

数据来源:


@Slf4j
@Servicepublic
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;
    }
}

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

import java.io.IOException;

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);
        } ...} ...
}

步骤四:定制一个HTTP的安全认证配置类(AbstractHttpConfigurer子类),将上一步定制的过滤器加入请求的过滤处理责任链。
步骤五:定义一个Spring Security安全配置类(WebSecurityConfigurerAdapter子类),对Web容器的HTTP的安全认证机制进行配置。

这一步有两项工作:一是应用DemoAuthConfigurer配置类;二是构造AuthenticationManagerBuilder认证管理者实例。定制类DemoWebSecurityConfig的代码如下:

@EnableWebSecuritypublic
class DemoWebSecurityConfig extends WebSecurityConfigurerAdapter {
    //配置HTTP请求的安全策略,应用DemoAuthConfigurer配置类实例
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable().and()
                .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();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值