专栏首页码农小胖哥的码农生涯Spring Security 实战干货:如何实现不同的接口不同的安全策略

Spring Security 实战干货:如何实现不同的接口不同的安全策略

1. 前言

欢迎阅读 Spring Security 实战干货 系列文章 。最近有开发小伙伴提了一个有趣的问题。他正在做一个项目,涉及两种风格,一种是给小程序出接口,安全上使用无状态的JWT Token;另一种是管理后台使用的是Freemarker,也就是前后端不分离的Session机制。用Spring Security该怎么办?

2. 解决方案

我们可以通过多次继承WebSecurityConfigurerAdapter构建多个HttpSecurityHttpSecurity 对象会告诉我们如何验证用户的身份,如何进行访问控制,采取的何种策略等等。

如果你看过之前的教程,我们是这么配置的:

/**
 * 单策略配置
 *
 * @author felord.cn
 * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
 * @since 14 :58 2019/10/15
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
@EnableWebSecurity
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class CustomSpringBootWebSecurityConfiguration {

    /**
     * The type Default configurer adapter.
     */
    @Configuration
    @Order(SecurityProperties.BASIC_AUTH_ORDER)
    static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            super.configure(auth);
        }

        @Override
        public void configure(WebSecurity web) {
            super.configure(web);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 配置 httpSecurity

        }
    }
}

上面的配置了一个HttpSecurity,我们如法炮制再增加一个WebSecurityConfigurerAdapter的子类来配置另一个HttpSecurity。伴随而来的还有不少的问题要解决。

2.1 如何路由不同的安全配置

我们配置了两个HttpSecurity之后,程序如何让小程序接口和后台接口走对应的HttpSecurity

HttpSecurity.antMatcher(String antPattern)可以提供过滤机制。比如我们配置:

     @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 配置 httpSecurity
            http.antMatcher("/admin/v1");

        }

那么该HttpSecurity将只提供给以/admin/v1开头的所有URL。这要求我们针对不同的客户端指定统一的URL前缀。

举一反三只要HttpSecurity提供的功能都可以进行个性化定制。比如登录方式,角色体系等。

2.2 如何指定默认的 HttpSecurity

我们可以通过在WebSecurityConfigurerAdapter实现上使用@Order注解来指定优先级,数值越大优先级越低,没有@Order注解将优先级最低。

2.3 如何配置不同的 UserDetailsService

很多情况下我们希望普通用户和管理用户完全隔离,我们就需要多个UserDetailsService,你可以在下面的方法中对AuthenticationManagerBuilder进行具体的设置来配置UserDetailsService,同时也可以配置不同的密码策略。

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
    daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            // 自行实现
            return  null ;
        }
    });
    // 也可以设计特定的密码策略
    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
    auth.authenticationProvider(daoAuthenticationProvider);
}

2.4 最终的配置模板

上面的几个问题解决之后,我们基本上掌握了在一个应用中执行多种安全策略。配置模板如下:

/**
 * 多个策略配置
 *
 * @author felord.cn
 * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
 * @since 14 :58 2019/10/15
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
@EnableWebSecurity
@ConditionalOnClass(WebSecurityConfigurerAdapter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class CustomSpringBootWebSecurityConfiguration {

    /**
     * 后台接口安全策略. 默认配置
     */
    @Configuration
    @Order(1)
    static class AdminConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
            //用户详情服务个性化
            daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                    // 自行实现
                    return null;
                }
            });
            // 也可以设计特定的密码策略
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
            auth.authenticationProvider(daoAuthenticationProvider);
        }

        @Override
        public void configure(WebSecurity web) {
            super.configure(web);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 根据需求自行定制
            http.antMatcher("/admin/v1")
                    .sessionManagement(Customizer.withDefaults())
                    .formLogin(Customizer.withDefaults());


        }
    }

    /**
     * app接口安全策略. 没有{@link Order}注解优先级比上面低
     */
    @Configuration
    static class AppConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
            //用户详情服务个性化
            daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                    // 自行实现
                    return null;
                }
            });
            // 也可以设计特定的密码策略
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
            auth.authenticationProvider(daoAuthenticationProvider);
        }

        @Override
        public void configure(WebSecurity web) {
            super.configure(web);
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 根据需求自行定制
            http.antMatcher("/app/v1")
                    .sessionManagement(Customizer.withDefaults())
                    .formLogin(Customizer.withDefaults());


        }
    }
}

3. 总结

本文分享自微信公众号 - 码农小胖哥(Felordcn),作者:码农小胖哥

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-06-11

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring居然还提供了这么好用的URL工具类

    开发中我们经常会操作 URL,比如提取端口、提取路径以及最常用的提取参数等等。很多时候需要借助于一些第三方类库或者自己编写工具类来实现,今天胖哥给大家介绍一种方...

    码农小胖哥
  • SpringBoot+Neo4j在社交电商中,讲述你是怎么被绑定为下线的

    社交电商中,现在做的比较出色的就应该属于花生日记,以及最近比较火的芬香。或许你经常会收到朋友或者亲戚发来的分享出的商品,而当你点击这些分享出来的商品后,那么,恭...

    码农小胖哥
  • Java 集合排序规则接口 Comparator

    最近用到了集合排序(基于 Java 8)。现在我能用 Stream 的就用 Stream ,真香!排序可以这么写:

    码农小胖哥
  • 让访客自动加自己为QQ好友代码

    Youngxj
  • 严选 | Elastic中文社区201903错题本

    开发最懊悔的事莫过于:自己费尽脑汁、花费了很长时间解决了问题,原来别人在社区或者别的地方早已经给出了更优化的方案。

    铭毅天下
  • 大数据处理过程之核心技术ETL详解

    核心技术架构挑战: 1、对现有数据库管理技术的挑战。 2、经典数据库技术并没有考虑数据的多类别(variety)、SQL(结构化数据查询语言),在设计的一开...

    钱塘数据
  • 共识算法三巨头的碰面

    一天,区块链共识算法的三巨头在蜂巢会上碰了碰头,一起探讨共识算法在区块链中的应用前景,三方各执一词,都觉得自己才是未来的老大。

    java达人
  • 手机号就能查电费!别说你还不知道!

    线上交电费虽然快捷方便,但输入户号一直是一个绕不过去的坎。 我们只能通过其它方式去找户号:电费单子,手机短信,机构网站……其次,动辄十几位数的户号在输入过程中...

    腾讯大讲堂
  • 一个基于TCP/IP的小项目,实现广播消息的功能。(超详细版)

    该功能基于上个项目的改进,主要是通过对服务器端代码的修改,以及对客户端作少许修改,实现开启多客户端时,一个客户端发送消息,达到对所有客户端广播的效果。可参考网吧...

    WeiMLing
  • 解决从旧格式的 csproj 迁移到新格式的 csproj 格式 AssemblyInfo 文件值重复问题 删除重复的特性不自动创建 AssemblyInfo 特性

    现在很多小伙伴开始使用了 dotnet core 项目,但是如果是从以前的 dotnet framework 的项目修改为 dotnet core 项目格式,会...

    林德熙

扫码关注云+社区

领取腾讯云代金券