AdSense

網頁

2019/9/30

Spring Boot Security two form api login with two UserDetailsService

本篇介紹如何在Spring Boot Security設定兩個form-data的api登入,且兩個登入口驗證時的使用者來源為不同的兩個UserDetailsService


先參考「Spring Boot Security two form api login範例」建立專案,然後修改Spring Security設定類別DemoWebSecurityConfig如下。
重點在各別的WebSecurityConfigurerAdapter類別都有自己的UserDetailsService實作,並透過各自的AuthenticationManagerBuilder設定。

OneLoginWebSecurityConfigurerAdapter驗證時從DemoOneUserDetailsService查詢使用者;
TwoLoginWebSecurityConfigurerAdapter驗證時從DemoTWoUserDetailsService查詢使用者。

package com.abc.demo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

@EnableWebSecurity
public class DemoSecurityConfig {

    @Configuration
    @Order(1)
    public static class OneLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private UserDetailsService demoOneUserDetailsService; // inject DemoOneUserDetailsService bean
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(demoOneUserDetailsService);
        }
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            
            http
                .antMatcher("/one/**")
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/one/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler( new DemoAuthenticationSuccessHandler() )
                .failureHandler( new DemoAuthenticationFailureHandler() )
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new DemoAuthenticationEntryPoint())
                .and()
                .csrf()
                .disable();
        }
    }
    
    @Configuration
    @Order(2)
    public static class TwoLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Autowired
        private UserDetailsService demoTwoUserDetailsService; // inject DemoTwoUserDetailsService bean
        
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(demoTwoUserDetailsService);
        }
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            
            http
                .antMatcher("/two/**")
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/two/login")
                .usernameParameter("username")
                .passwordParameter("password")
                .successHandler( new DemoAuthenticationSuccessHandler() )
                .failureHandler( new DemoAuthenticationFailureHandler() )
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new DemoAuthenticationEntryPoint())
                .and()
                .csrf()
                .disable();
        }
    }
}

接著建立兩個UserDetailsService的實作,
DemoOneUserDetailsServiceDemoTwoUserDetailsService

DemoOneUserDetailsService的使用者為帳密為one123/one123

DemoOneUserDetailsService

package com.abc.demo.config;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class DemoOneUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        
        return User.withUsername("one123")
                .password("{noop}one123")
                .roles("USER")
                .build();
    }

}

DemoTwoUserDetailsService的使用者為帳密為two123/two123

DemoTwoUserDetailsService

package com.abc.demo.config;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class DemoTwoUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        
        return User.withUsername("two123")
                .password("{noop}two123")
                .roles("USER")
                .build();
    }

}

完成全部設定後的專案目錄結構如下




開啟Postman來進行登入測試。

在Postman輸入POSThttp://localhost:8080/demo/one/login並夾帶下面username/password從登入點一登入

KEYVALUE
usernameone123
passwordone123

在Postman輸入POSThttp://localhost:8080/demo/two/login並夾帶下面username/password從登入點二登入

KEYVALUE
usernametwo123
passwordtwo123

登入配置一的使用者帳密無法通過登入配置二的驗證,反之亦然。

也就是說,
能通過登入口一驗證的使用者僅限於DemoOneUserDetailsService的使用者。
能通過登入口二驗證的使用者僅限於DemoTwoUserDetailsService的使用者。


不過一旦通過驗證並成功登入,不論是從哪個登入點登入都可以存取另一個登入配置所保護的路徑資源。
例如成功登入POSThttp://localhost:8080/demo/one/login的使用者也可存取
POSThttp://localhost:8080/demo/two/resource的資源。


參考:

沒有留言:

AdSense