<dependency>
? ? <groupId>org.springframework.boot</groupId>
? ? <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
? ? <groupId>org.springframework.security.oauth</groupId>
? ? <artifactId>spring-security-oauth2</artifactId>
? ? <version>2.1.3.RELEASE</version>
</dependency>
<dependency>
? ? <groupId>org.springframework.boot</groupId>
? ? <artifactId>spring-boot-starter-security</artifactId>
</dependency>
這里有兩個核心組件依賴:OAuth2組件和Security組件。
模塊劃分
auth-server:授權服務
resource-server:資源服務器
third-server:第三個服務
2、配置描述
【授權服務】
OAuth2配置
這里的配置管理的是第三方的授權流程和發放給第三方的身份證明ClientID和密碼,實際的場景就是第三方借助郵箱賬號登錄,首先就是向郵箱管理方提供材料,獲取訪問郵箱服務的身份證明,然后才能對接開放服務,這種模式在第三方對接業務中很常見。
/**
* 模擬第三方授權配置
*/
@EnableAuthorizationServer
@Configuration
public class AuthConfig extends AuthorizationServerConfigurerAdapter {
? ? @Resource
? ? ClientDetailsService clientDetailsService;
? ? /**
? ? * 資源服務器校驗Token
? ? */
? ? @Override
? ? public void configure(AuthorizationServerSecurityConfigurer security) {
? ? ? ? security.checkTokenAccess("permitAll()").allowFormAuthenticationForClients();
? ? }
? ? /**
? ? * 第三方客戶端請求配置,和資源服務訪問的配置,不設置默認都可以訪問,提供默認回調地址
? ? */
? ? @Override
? ? public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
? ? ? ? clients.inMemory()
? ? ? ? ? ? ? ? .withClient("third01")
? ? ? ? ? ? ? ? .secret(new BCryptPasswordEncoder().encode("third01"))
? ? ? ? ? ? ? ? .resourceIds("resource-01")
? ? ? ? ? ? ? ? .authorizedGrantTypes("authorization_code","refresh_token")
? ? ? ? ? ? ? ? .scopes("all")
? ? ? ? ? ? ? ? .redirectUris("http://localhost:8082/notify.html");
? ? }
? ? /**
? ? * 配置訪問端點
? ? */
? ? @Override
? ? public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
? ? ? ? endpoints.authorizationCodeServices(authorizationCodeServices()).tokenServices(tokenServices());
? ? }
? ? /**
? ? * 內存管理
? ? */
? ? @Bean
? ? AuthorizationCodeServices authorizationCodeServices() {
? ? ? ? return new InMemoryAuthorizationCodeServices();
? ? }
? ? /**
? ? * Token管理規則
? ? */
? ? @Bean
? ? AuthorizationServerTokenServices tokenServices() {
? ? ? ? DefaultTokenServices services = new DefaultTokenServices();
? ? ? ? services.setClientDetailsService(clientDetailsService);
? ? ? ? services.setSupportRefreshToken(true);
? ? ? ? services.setTokenStore(tokenStore());
? ? ? ? services.setAccessTokenValiditySeconds(3600);
? ? ? ? services.setRefreshTokenValiditySeconds(3600*7);
? ? ? ? return services;
? ? }
? ? @Bean
? ? TokenStore tokenStore() {
? ? ? ? return new InMemoryTokenStore();
? ? }
}
通常需要數據庫存儲第三方信息,可以到第OAuth2開源項目中,獲取表結構放到本地數據庫中,然后這里換成數據源加載模式即可,簡單的流程管理都在源碼里寫了SQL語句,數據源引入即可。
Security配置
/**
* 模擬本地用戶配置
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
? ? /**
? ? * 密碼加密方式
? ? */
? ? @Bean
? ? public PasswordEncoder passwordEncoder(){
? ? ? ? return new BCryptPasswordEncoder();
? ? }
? ? /**
? ? * 內存中虛擬用戶和角色
? ? */
? ? @Override
? ? protected void configure(AuthenticationManagerBuilder auth) throws Exception {
? ? ? ? auth.inMemoryAuthentication()
? ? ? ? ? ? ? ? .withUser("user")
? ? ? ? ? ? ? ? .password(new BCryptPasswordEncoder().encode("123456"))
? ? ? ? ? ? ? ? .roles("user");
? ? }
? ? /**
? ? * 表單登錄
? ? */
? ? @Override
? ? protected void configure(HttpSecurity http) throws Exception {
? ? ? ? http.csrf().disable().formLogin();
? ? }
}
基于這里的配置管理郵箱用戶的認證流程,例如使用郵箱賬號密碼登錄驗證,判斷授權是否成立,這里管理的是服務本地的郵箱賬號,基于數據源存儲數據在下面案例中都有。
案例一:JWT組件管理身份驗證機制
案例二:Shiro組件實現用戶權限管理
案例三:Security用戶安全認證流程
關于Spring框架中安全認證的相關的幾個組件,在使用OAuth2之前可以先了解一下。
【資源服務】
主要功能有三塊,配置第三方攜帶的Token身份令牌校驗機制,即訪問授權服務校驗接口,這里是OAuth2自定義好的接口;配置resourceId資源服務的編號,用來控制第三個服務能訪問的資源服務范圍,屬于大的權限點控制;模擬校驗用戶的Role角色,較精細的控制權限。
/**
* 資源服務管理配置
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
? ? /**
? ? * Token令牌校驗
? ? */
? ? @Bean
? ? RemoteTokenServices tokenServices() {
? ? ? ? RemoteTokenServices services = new RemoteTokenServices();
? ? ? ? services.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
? ? ? ? services.setClientId("third01");
? ? ? ? services.setClientSecret("third01");
? ? ? ? return services;
? ? }
? ? /**
? ? * 服務資源ID配置
? ? */
? ? @Override
? ? public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
? ? ? ? resources.resourceId("resource-01").tokenServices(tokenServices());
? ? }
? ? /**
? ? * 模擬用戶權限規則
? ? */
? ? @Override
? ? public void configure(HttpSecurity http) throws Exception {
? ? ? ? http.authorizeRequests()
? ? ? ? ? ? ? ? .antMatchers("/user/**").hasRole("user")
? ? ? ? ? ? ? ? .anyRequest().authenticated();
? ? }
}
【第三方服務】
主要提供兩個流程的模擬:請求授權服務獲取身份令牌;攜帶身份令牌請求資源服務獲取數據。這里則是授權碼回調接口的處理方式。
@Controller
public class NotifyController {
? ? private static final Logger LOG = LoggerFactory.getLogger(NotifyController.class);
? ? @Resource
? ? private RestTemplate restTemplate;
? ? @GetMapping("/notify.html")
? ? public String notify(String code, Model model) {
? ? ? ? if (code != null) {
? ? ? ? ? ? MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
? ? ? ? ? ? map.add("code", code);
? ? ? ? ? ? map.add("client_id", "third01");
? ? ? ? ? ? map.add("client_secret", "third01");
? ? ? ? ? ? map.add("redirect_uri", "http://localhost:8082/notify.html");
? ? ? ? ? ? map.add("grant_type", "authorization_code");
? ? ? ? ? ? Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
? ? ? ? ? ? String accessToken = resp.get("access_token");
? ? ? ? ? ? LOG.info("身份令牌:{}",accessToken);
? ? ? ? ? ? HttpHeaders headers = new HttpHeaders();
? ? ? ? ? ? headers.add("Authorization", "Bearer " + accessToken);
? ? ? ? ? ? HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
? ? ? ? ? ? ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/user/resource", HttpMethod.GET, httpEntity, String.class);
? ? ? ? ? ? model.addAttribute("notifyMsg", entity.getBody());
? ? ? ? }
? ? ? ? return "notify";
? ? }
}
三、測試流程
通過上述測試流程,對比常見的第三方登錄機制,理解OAuth2的授權碼模式。
四、源代碼地址
GitHub·地址
https://github.com/cicadasmile/middle-ware-parent
GitEE·地址
https://gitee.com/cicadasmile/middle-ware-parent
龍華大道1號 http://www.kinghill.cn/Dynamics/2106.html