Spring Security 之API 項目安全驗證(基於basic-authentication)


===================================
Basic Authorization 規范
===================================
Request 頭部:
Authorization: Basic QWxpY2U6MTIzNDU2
其中 QWxpY2U6MTIzNDU2 是user:pwd做 base64 編碼, 格式是 user:pwd

response 頭部:
WWW-Authenticate: Basic realm="My Realm"

按照 RFC 規范, 相同的 realm(域) 下的web page 將共享同樣的 credentials, 所以推薦 realm 取值為 application name. realm 大小寫敏感, 可以包含空格.


===================================
Rest API 示例
===================================
功能:
1. 演示如何啟用 Basic Authorization
2. 如何使用 RestTemplate 訪問受保護的 API接口

-----------------------------------
SecurityConfig 代碼
-----------------------------------
關鍵點有:
0. 對於 /api/** 需要 ROLE_ADMIN 角色的賬號訪問, 對於 /guest/** 路徑允許匿名訪問.
1. 使用 HttpSecurity.httpBasic() 啟用 Basic Authorization.
2. 使用 HttpSecurity.httpBasic().realmName() 設置 realm.
3. 使用 HttpSecurity.httpBasic().authenticationEntryPoint() 設置 BasicAuthenticationEntryPoint 對象, 如果一個請求通過驗證, 該對象會自動為web response設定 WWW-Authenticate header, 如果未通過, 該對象會自動將HttpStatus設置為UNAUTHORIZED.
4. 顯式啟用了 STATELESS session 管理機制, 經測試,Spring Security 在Basic Authorization模式下, session自動就處於了 STATELESS 狀態.
5. 對於 HttpMethod.OPTIONS 請求, 允許匿名訪問. API 項目應該開放 OPTIONS 查詢權限.

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //@formatter:off
    @Override
    public void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.inMemoryAuthentication()
                .withUser("123").password("123").roles("USER")
                .and()
                .withUser("ADMIN").password("ADMIN").roles("ADMIN");
    }
    //@formatter:on

    private static String REALM = "MY SPRING SECURITY DEMO";

    // @formatter:off
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
           .authorizeRequests()
               // 對於/api 路徑下的訪問需要有 ROLE_ADMIN 的權限
              .antMatchers("/api/**").hasRole("ADMIN")
               // 對於/guest 路徑開放訪問
              .antMatchers("/guest/**").permitAll()
               // 其他url路徑之需要登陸即可.
              .anyRequest().authenticated()
              .and()
           //啟用 basic authentication
          .httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthenticationEntryPoint())
              .and()
           //不創建 session
          .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    // @formatter:on

    /**
     * 生成 BasicAuthenticationEntryPoint 對象, 在該對象的支持下, 通過驗證的請求, 返回的response 將會自動加上
     * WWW-Authenticate Header.  在該對象的支持下, 未通過驗證的請求, 返回的 response 為 UNAUTHORIZED 錯誤.
     *
     * @return
     */
    @Bean
    public BasicAuthenticationEntryPoint getBasicAuthenticationEntryPoint() {
        BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
        entryPoint.setRealmName(REALM);
        return entryPoint;
    }

    /*
     * 開放 Options 請求
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // TODO Auto-generated method stub
        web.ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @SuppressWarnings("deprecation")
    @Bean
    public NoOpPasswordEncoder passwordEncoder() {
        BasicAuthenticationEntryPoint a;
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
}


-----------------------------------
受控 API Rest 類
-----------------------------------
對於 /api/** url需要 ROLE_ADMIN 角色的賬號訪問, 這里的代碼很簡單.

/*
 * 需要進行 Basic Auth 的API
 */
@RestController
@RequestMapping("/api")
class ApiController {
@GetMapping(
"/books") public String getBooks() { return "API book"; } }

直接訪問受控url, 彈出瀏覽器內置的登陸框, 見下圖, 符合預期. 

 


-----------------------------------
RestTemplate 訪問 Basic Authorization的API
-----------------------------------
關鍵點:
使用了 restTemplateBuilder.basicAuthorization(user,pwd).build() 來構建 RestTemplate, 這樣的 RestTemplate 會自動在Request上加 Authorization Header.

 

/*
 * RestTemplate 訪問 Basic Authorization的API的示例
 */
@RestController
@RequestMapping("/guest")
class DefaultController {

    @GetMapping("/mybooks")
    @ResponseBody
    public String getMyBook() {
        return "My Book";
    }

    private RestTemplate restTemplate;

    @Autowired
    private RestTemplateBuilder restTemplateBuilder;

    @Autowired
    void setRestTemplate(RestTemplateBuilder restTemplateBuilder) {
         restTemplate = restTemplateBuilder.basicAuthorization("ADMIN", "ADMIN")
         .build();
    }

    @GetMapping("/apibooks")
    @ResponseBody
    public String getApiBooks() {
        return restTemplate.getForObject("http://localhost:8080/api/books", String.class);

    }
}

訪問 http://localhost:8080/guest/apibooks 地址,  無需登陸即可得到 api 結果, 見下圖, 符合預期. 

 

===================================
參考
===================================
http://websystique.com/spring-security/secure-spring-rest-api-using-basic-authentication/
http://www.bytestree.com/spring/restful-web-services-authentication-authorization/
https://www.baeldung.com/spring-security-basic-authentication


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM