===================================
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
