8 months ago

如果要取得 OAuth 授權的話,可以直接使用 OkHttpClient 或是 OAuth2RestTemplate 來實作

在依賴中增加 OkHttpClient

build.gradle
dependencies {
    compile 'com.squareup.okhttp3:okhttp:3.6.0'
}

實際登入的程式

public void Okhttp() throws IOException {
    OkHttpClient client = new OkHttpClient();
    String credential = Credentials.basic("clientkpi", "123456");
    FormBody body = new FormBody.Builder()
            .add("username", "sam.chu=")
            .add("password", "12345678")
            .add("grant_type", "password")
            .add("scope", "account role").build();
    Request request = new Request.Builder()
            .header("Authorization", credential)
            .url("http://localhost:8081/oauth/token")
            .post(body)
            .build();
    try (Response response = client.newCall(request).execute()) {
        if (!response.isSuccessful()) {
            throw new IOException("Unexpected code " + response);
        }
        ObjectMapper mapper = new ObjectMapper();
        OauthToken oauthToken = mapper.readValue(response.body().string(), OauthToken.class);
        System.out.println(oauthToken);
    }
}

或是你可以選 org.springframework.security.oauth:spring-security-oauth2 提供的 OAuth2RestTemplate

在依賴中增加 org.springframework.security.oauth:spring-security-oauth2

build.gradle
dependencies {
    compile('org.springframework.boot:spring-boot-starter-security')
    compile 'org.springframework.security.oauth:spring-security-oauth2:2.0.12.RELEASE'
}
public void testOAuth() {
    AccessTokenRequest atr = new DefaultAccessTokenRequest();
    OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(atr));
    try {
        System.out.println(oAuth2RestTemplate.getAccessToken());
        System.out.println(oAuth2RestTemplate.getAccessToken().getRefreshToken());
    } catch (OAuth2AccessDeniedException e) {
        System.out.println("登入失敗" + e.getHttpErrorCode()); // 403

        System.out.println("登入失敗" + e.getOAuth2ErrorCode()); // access_denied

        System.out.println("登入失敗" + e.getMessage()); //Access token denied.

        System.out.println("登入失敗" + e.getLocalizedMessage()); // Access token denied.

        System.out.println("登入失敗" + e.getSummary()); // error="access_denied", error_description="Access token denied."

    }
}

protected OAuth2ProtectedResourceDetails resource() {
    ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
    List scopes = new ArrayList<String>(2);
    scopes.add("account");
    scopes.add("role");
    resource.setAccessTokenUri("http://localhost:8081/oauth/token");
    resource.setClientId("clientkpi");
    resource.setClientSecret("123456");
    resource.setGrantType("password");
    resource.setScope(scopes);
    resource.setUsername("sam.chu=");
    resource.setPassword("12345678");
    return resource;
}

不過基於 Spring Boot 的自動配置會亂數產生帳密 來做 Http basic 認證,剛開發的時候會有點煩
可以在 SpringBootApplication 暫時把它關閉
順便把 EnableOAuth2Client 加上

@EnableOAuth2Client
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class KpiApplication {

    public static void main(String[] args) {
        SpringApplication.run(KpiApplication.class, args);
    }
}

其他兩個範例是使用 RestTemplate 來新增跟取得的範例

public void restPost() throws IOException {
        String url = "http://localhost:8082/api/v1/role";
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
        headers.add("Authorization", String.format("%s %s", "bearer", "eyJhbGciOiJIUzI1.NiIsInR5cCI6I.kpXVCJ9"));
        RoleDto roleDto = new RoleDto();
        roleDto.setCode("ROLE_GINTAMA");
        roleDto.setLabel("銀魂銀魂銀魂");
        HttpEntity<RoleDto> entity = new HttpEntity<RoleDto>(roleDto, headers);
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, entity, String.class, new HashMap<String, String>());
        if (response.getStatusCode().equals(HttpStatus.CREATED)) {
            System.out.println(response.getBody());
        }
    }

取得資源

public void restGet() throws IOException {
    String url = "http://localhost:8082/api/v1/role";
    RestTemplate restTemplate = new RestTemplate();
    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
    headers.add("Authorization", String.format("%s %s", "bearer", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsia3BpIiwiYWNjb3VudCJdLCJ1c2VyX25hbWUiOiJzYW0uY2h1PSIsInNjb3BlIjpbImFjY291bnQiLCJyb2xlIl0sImV4cCI6MTQ4NzU3NDUzNywiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6ImNiMzc0OGFmLTc2NGUtNDNiNy1iNTVjLTU4ZjQzZWQwOTU0MCIsImNsaWVudF9pZCI6ImNsaWVudGtwaSJ9.9Wwk5-GrJ_xdVOcOexoDhIXEznHqm3ssBfob0FeSFgA"));
    HttpEntity<String> entity = new HttpEntity<String>(headers);
    ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
    if (response.getStatusCode().equals(HttpStatus.OK)) {
        System.out.println(response.getBody());
    }
}

或是你可以採用 Spring Cloud 已經整合的聲明式 Feign
依賴加上

build.gradle
dependencies {
    compile 'org.springframework.boot:spring-boot-starter-hateoas:1.5.1.RELEASE'
    compile 'org.springframework.cloud:spring-cloud-starter-feign:1.2.5.RELEASE'
}

主程式啟用 EnableFeignClients 跟 EnableHypermediaSupport

EnableHypermediaSupport 是因為你跟 SpringDataRest 拿資料的時候會有所謂連結資訊,不加的話他無法解析

@EnableRetry
@EnableOAuth2Client
@EnableFeignClients
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class KpiApplication {

    public static void main(String[] args) {
        SpringApplication.run(KpiApplication.class, args);
    }
}

設定檔配置實體位置

application.yml
accountReader.url: http://localhost:8082

FeignClient

RoleReader.java
import com.ps.dto.RoleDto;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Created by samchu on 2017/2/23.
 */
@FeignClient(name = "account-service", url = "${accountReader.url}")
public interface RoleReader {
    // 新增

    @RequestMapping(method = RequestMethod.POST, value = "/api/v1/role", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    RoleDto create(@RequestHeader("Authorization") String token, RoleDto roleDto);
    // 取得角色的分頁(使用 Spring Data Rest)

    @RequestMapping(method = RequestMethod.GET, value = "roles", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    PagedResources<Resource<RoleDto>> getRoles(@RequestHeader("Authorization") String token);
    // 取得角色的資訊(使用 Spring Data Rest)

    @RequestMapping(method = RequestMethod.GET, value = "roles/{id}", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    Resource<RoleDto> getRole(@RequestHeader("Authorization") String token, @PathVariable("id") String id);
}

之後就可以像在使用一般套件直接操作遠端

String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYWNjb3VudCJdLCJ1c2VyX25hbWUiOiJzYW0uY2h1MCIsInNjb3BlIjpbImFjY291bnQiLCJyb2xlIl0sImV4cCI6MTQ4NzgzNDk4MywiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjJkMmUzNDViLTQ1MjgtNDdjNy05Zjg5LWZjNGJiNjdlM2MxOSIsImNsaWVudF9pZCI6ImNsaWVudGFwcCJ9.AWGe-XTp-0z2jD0l69oLks1reeKhqof9GE5GzT38bZU";
System.out.println(roleReader.getRole("bearer " + token, "4C0T0bs9Gd"));
RoleDto roleDto = new RoleDto();
roleDto.setCode("ROLE_SAM");
roleDto.setLabel("測試用的啦阿");
roleReader.create("bearer " + token, roleDto);
← use JWT OAuth2 and spring-security Create AuthorizationServer SpringBoot Build War →
 
comments powered by Disqus