Spring Security 實戰乾貨:如何實現不一樣的接口不一樣的安全策略

1. 前言

歡迎閱讀 Spring Security 實戰乾貨 系列文章 。最近有開發小夥伴提了一個有趣的問題。他正在作一個項目,涉及兩種風格,一種是給小程序出接口,安全上使用無狀態的JWT Token;另外一種是管理後臺使用的是Freemarker,也就是先後端不分離的Session機制。用Spring Security該怎麼辦?html

2. 解決方案

咱們能夠經過屢次繼承WebSecurityConfigurerAdapter構建多個HttpSecurityHttpSecurity 對象會告訴咱們如何驗證用戶的身份,如何進行訪問控制,採起的何種策略等等。java

若是你看過以前的教程,咱們是這麼配置的:web

/**
 * 單策略配置
 *
 * @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。伴隨而來的還有很多的問題要解決。spring

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註解將優先級最低。session

2.3 如何配置不一樣的UserDetailsService

不少狀況下咱們但願普通用戶和管理用戶徹底隔離,咱們就須要多個UserDetailsService,你能夠在下面的方法中對AuthenticationManagerBuilder進行具體的設置來配置UserDetailsService,同時也能夠配置不一樣的密碼策略。app

@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 最終的配置模板

上面的幾個問題解決以後,咱們基本上掌握了在一個應用中執行多種安全策略。配置模板以下:ide

/**
 * 多個策略配置
 *
 * @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 獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索