mirror of
https://github.com/czp3009/bilibili-api.git
synced 2025-03-21 21:00:26 +08:00
完成 sso API
This commit is contained in:
parent
4694899ef9
commit
4e954af9d2
@ -10,7 +10,6 @@ apply plugin: 'signing'
|
|||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
|
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package com.hiczp.bilibili.api;
|
package com.hiczp.bilibili.api;
|
||||||
|
|
||||||
|
import com.hiczp.bilibili.api.cookie.SimpleCookieJar;
|
||||||
import com.hiczp.bilibili.api.interceptor.*;
|
import com.hiczp.bilibili.api.interceptor.*;
|
||||||
import com.hiczp.bilibili.api.live.LiveService;
|
import com.hiczp.bilibili.api.live.LiveService;
|
||||||
import com.hiczp.bilibili.api.live.socket.LiveClient;
|
import com.hiczp.bilibili.api.live.socket.LiveClient;
|
||||||
import com.hiczp.bilibili.api.passport.PassportService;
|
import com.hiczp.bilibili.api.passport.PassportService;
|
||||||
|
import com.hiczp.bilibili.api.passport.SsoService;
|
||||||
import com.hiczp.bilibili.api.passport.entity.InfoEntity;
|
import com.hiczp.bilibili.api.passport.entity.InfoEntity;
|
||||||
import com.hiczp.bilibili.api.passport.entity.LoginResponseEntity;
|
import com.hiczp.bilibili.api.passport.entity.LoginResponseEntity;
|
||||||
import com.hiczp.bilibili.api.passport.entity.LogoutResponseEntity;
|
import com.hiczp.bilibili.api.passport.entity.LogoutResponseEntity;
|
||||||
import com.hiczp.bilibili.api.passport.entity.RefreshTokenResponseEntity;
|
import com.hiczp.bilibili.api.passport.entity.RefreshTokenResponseEntity;
|
||||||
import com.hiczp.bilibili.api.passport.exception.CaptchaMismatchException;
|
import com.hiczp.bilibili.api.passport.exception.CaptchaMismatchException;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.*;
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor;
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -18,6 +19,7 @@ import retrofit2.Retrofit;
|
|||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.security.auth.login.LoginException;
|
import javax.security.auth.login.LoginException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@ -25,8 +27,9 @@ import java.time.Instant;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider {
|
public class BilibiliAPI implements BilibiliServiceProvider, BilibiliSsoProvider, LiveClientProvider {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(BilibiliAPI.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(BilibiliAPI.class);
|
||||||
|
|
||||||
private final Long apiInitTime = Instant.now().getEpochSecond(); //记录当前类被实例化的时间
|
private final Long apiInitTime = Instant.now().getEpochSecond(); //记录当前类被实例化的时间
|
||||||
@ -72,8 +75,6 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
public PassportService getPassportService(@Nonnull List<Interceptor> interceptors, @Nonnull HttpLoggingInterceptor.Level logLevel) {
|
public PassportService getPassportService(@Nonnull List<Interceptor> interceptors, @Nonnull HttpLoggingInterceptor.Level logLevel) {
|
||||||
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
|
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
|
||||||
|
|
||||||
interceptors.forEach(okHttpClientBuilder::addInterceptor);
|
|
||||||
|
|
||||||
okHttpClientBuilder
|
okHttpClientBuilder
|
||||||
.addInterceptor(new AddFixedParamsInterceptor(
|
.addInterceptor(new AddFixedParamsInterceptor(
|
||||||
"build", bilibiliClientProperties.getBuild(),
|
"build", bilibiliClientProperties.getBuild(),
|
||||||
@ -85,7 +86,11 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
))
|
))
|
||||||
.addInterceptor(new AddAppKeyInterceptor(bilibiliClientProperties))
|
.addInterceptor(new AddAppKeyInterceptor(bilibiliClientProperties))
|
||||||
.addInterceptor(new SortParamsAndSignInterceptor(bilibiliClientProperties))
|
.addInterceptor(new SortParamsAndSignInterceptor(bilibiliClientProperties))
|
||||||
.addInterceptor(new ErrorResponseConverterInterceptor())
|
.addInterceptor(new ErrorResponseConverterInterceptor());
|
||||||
|
|
||||||
|
interceptors.forEach(okHttpClientBuilder::addInterceptor);
|
||||||
|
|
||||||
|
okHttpClientBuilder
|
||||||
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(logLevel));
|
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(logLevel));
|
||||||
|
|
||||||
return new Retrofit.Builder()
|
return new Retrofit.Builder()
|
||||||
@ -107,8 +112,6 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
public LiveService getLiveService(@Nonnull List<Interceptor> interceptors, @Nonnull HttpLoggingInterceptor.Level logLevel) {
|
public LiveService getLiveService(@Nonnull List<Interceptor> interceptors, @Nonnull HttpLoggingInterceptor.Level logLevel) {
|
||||||
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
|
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
|
||||||
|
|
||||||
interceptors.forEach(okHttpClientBuilder::addInterceptor);
|
|
||||||
|
|
||||||
okHttpClientBuilder
|
okHttpClientBuilder
|
||||||
.addInterceptor(new AddFixedHeadersInterceptor(
|
.addInterceptor(new AddFixedHeadersInterceptor(
|
||||||
"Buvid", bilibiliClientProperties.getBuvId(),
|
"Buvid", bilibiliClientProperties.getBuvId(),
|
||||||
@ -144,7 +147,11 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
))
|
))
|
||||||
.addInterceptor(new AddAccessKeyInterceptor(bilibiliAccount))
|
.addInterceptor(new AddAccessKeyInterceptor(bilibiliAccount))
|
||||||
.addInterceptor(new SortParamsAndSignInterceptor(bilibiliClientProperties))
|
.addInterceptor(new SortParamsAndSignInterceptor(bilibiliClientProperties))
|
||||||
.addInterceptor(new ErrorResponseConverterInterceptor())
|
.addInterceptor(new ErrorResponseConverterInterceptor());
|
||||||
|
|
||||||
|
interceptors.forEach(okHttpClientBuilder::addInterceptor);
|
||||||
|
|
||||||
|
okHttpClientBuilder
|
||||||
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(logLevel));
|
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(logLevel));
|
||||||
|
|
||||||
return new Retrofit.Builder()
|
return new Retrofit.Builder()
|
||||||
@ -155,6 +162,45 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
.create(LiveService.class);
|
.create(LiveService.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SsoService getSsoService() {
|
||||||
|
return getSsoService(new SimpleCookieJar());
|
||||||
|
}
|
||||||
|
|
||||||
|
//sso 需要保存 cookie, 不对 SsoService 进行缓存
|
||||||
|
@Override
|
||||||
|
public SsoService getSsoService(CookieJar cookieJar) {
|
||||||
|
return getSsoService(cookieJar, Collections.emptyList(), HttpLoggingInterceptor.Level.BASIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SsoService getSsoService(@Nonnull CookieJar cookieJar, @Nonnull List<Interceptor> interceptors, @Nonnull HttpLoggingInterceptor.Level logLevel) {
|
||||||
|
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
|
||||||
|
|
||||||
|
okHttpClientBuilder
|
||||||
|
.cookieJar(cookieJar)
|
||||||
|
.addInterceptor(new AddFixedParamsInterceptor(
|
||||||
|
"build", bilibiliClientProperties.getBuild(),
|
||||||
|
"mobi_app", "android",
|
||||||
|
"platform", "android"
|
||||||
|
))
|
||||||
|
.addInterceptor(new AddDynamicParamsInterceptor(
|
||||||
|
() -> "ts", () -> Long.toString(Instant.now().getEpochSecond())
|
||||||
|
))
|
||||||
|
.addInterceptor(new AddAccessKeyInterceptor(bilibiliAccount))
|
||||||
|
.addInterceptor(new AddAppKeyInterceptor(bilibiliClientProperties))
|
||||||
|
.addInterceptor(new SortParamsAndSignInterceptor(bilibiliClientProperties));
|
||||||
|
|
||||||
|
interceptors.forEach(okHttpClientBuilder::addInterceptor);
|
||||||
|
|
||||||
|
okHttpClientBuilder
|
||||||
|
.addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(logLevel));
|
||||||
|
|
||||||
|
return new Retrofit.Builder()
|
||||||
|
.baseUrl(BaseUrlDefinition.PASSPORT)
|
||||||
|
.client(okHttpClientBuilder.build())
|
||||||
|
.build()
|
||||||
|
.create(SsoService.class);
|
||||||
|
}
|
||||||
|
|
||||||
public LoginResponseEntity login(@Nonnull String username, @Nonnull String password) throws IOException, LoginException, CaptchaMismatchException {
|
public LoginResponseEntity login(@Nonnull String username, @Nonnull String password) throws IOException, LoginException, CaptchaMismatchException {
|
||||||
return login(username, password, null, null);
|
return login(username, password, null, null);
|
||||||
}
|
}
|
||||||
@ -270,6 +316,30 @@ public class BilibiliAPI implements BilibiliServiceProvider, LiveClientProvider
|
|||||||
return infoEntity;
|
return infoEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpUrl getSsoUrl(@Nullable String goUrl) {
|
||||||
|
CancelRequestInterceptor cancelRequestInterceptor = new CancelRequestInterceptor();
|
||||||
|
try {
|
||||||
|
getSsoService(new SimpleCookieJar(), Collections.singletonList(cancelRequestInterceptor), HttpLoggingInterceptor.Level.BASIC)
|
||||||
|
.sso(null)
|
||||||
|
.execute();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
|
||||||
|
}
|
||||||
|
return cancelRequestInterceptor.getRequest().url();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<Cookie>> toCookies() throws IOException {
|
||||||
|
return toCookies(BaseUrlDefinition.PASSPORT + "api/oauth2/getKey");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<Cookie>> toCookies(@Nullable String goUrl) throws IOException {
|
||||||
|
SimpleCookieJar simpleCookieJar = new SimpleCookieJar();
|
||||||
|
getSsoService(simpleCookieJar).sso(goUrl).execute();
|
||||||
|
return simpleCookieJar.getCookiesMap();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LiveClient getLiveClient(long showRoomId) {
|
public LiveClient getLiveClient(long showRoomId) {
|
||||||
return bilibiliAccount.getUserId() == null ?
|
return bilibiliAccount.getUserId() == null ?
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package com.hiczp.bilibili.api;
|
||||||
|
|
||||||
|
import com.hiczp.bilibili.api.passport.SsoService;
|
||||||
|
import okhttp3.Cookie;
|
||||||
|
import okhttp3.CookieJar;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface BilibiliSsoProvider {
|
||||||
|
SsoService getSsoService(CookieJar cookieJar);
|
||||||
|
|
||||||
|
//获取用于进行 sso 登录的初始 URL
|
||||||
|
HttpUrl getSsoUrl(String goUrl);
|
||||||
|
|
||||||
|
//获取当前 token 对应的 cookies
|
||||||
|
Map<String, List<Cookie>> toCookies() throws IOException;
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.hiczp.bilibili.api.cookie;
|
||||||
|
|
||||||
|
import okhttp3.Cookie;
|
||||||
|
import okhttp3.CookieJar;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SimpleCookieJar implements CookieJar {
|
||||||
|
private Map<String, List<Cookie>> cookiesMap;
|
||||||
|
|
||||||
|
public SimpleCookieJar() {
|
||||||
|
cookiesMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleCookieJar(Map<String, List<Cookie>> cookiesMap) {
|
||||||
|
this.cookiesMap = new HashMap<>(cookiesMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||||||
|
cookies.forEach(cookie -> {
|
||||||
|
String domain = cookie.domain();
|
||||||
|
List<Cookie> savedCookies = cookiesMap.get(domain);
|
||||||
|
if (savedCookies == null) {
|
||||||
|
savedCookies = new ArrayList<>();
|
||||||
|
savedCookies.add(cookie);
|
||||||
|
cookiesMap.put(domain, savedCookies);
|
||||||
|
} else {
|
||||||
|
for (Cookie savedCookie : savedCookies) {
|
||||||
|
if (savedCookie.name().equals(cookie.name())) {
|
||||||
|
savedCookies.remove(savedCookie);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
savedCookies.add(cookie);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||||
|
return getCookiesForHost(url.host());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Cookie> getCookiesForHost(String host) {
|
||||||
|
List<Cookie> cookieList = new ArrayList<>();
|
||||||
|
cookiesMap.forEach((domain, cookies) -> {
|
||||||
|
if (host.endsWith(domain)) {
|
||||||
|
for (int i = cookies.size() - 1; i >= 0; i--) {
|
||||||
|
if (cookies.get(i).expiresAt() < System.currentTimeMillis()) {
|
||||||
|
cookies.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cookieList.addAll(cookies);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return cookieList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, List<Cookie>> getCookiesMap() {
|
||||||
|
return cookiesMap;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.hiczp.bilibili.api.exception;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class UserCancelRequestException extends IOException {
|
||||||
|
public UserCancelRequestException() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCancelRequestException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCancelRequestException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UserCancelRequestException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.hiczp.bilibili.api.interceptor;
|
||||||
|
|
||||||
|
import com.hiczp.bilibili.api.exception.UserCancelRequestException;
|
||||||
|
import okhttp3.Interceptor;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class CancelRequestInterceptor implements Interceptor {
|
||||||
|
private Request request;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response intercept(Chain chain) throws IOException {
|
||||||
|
request = chain.request();
|
||||||
|
throw new UserCancelRequestException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Request getRequest() {
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,6 @@ import retrofit2.http.POST;
|
|||||||
import retrofit2.http.Query;
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public interface PassportService {
|
public interface PassportService {
|
||||||
//获取验证码
|
//获取验证码
|
||||||
@ -50,9 +49,4 @@ public interface PassportService {
|
|||||||
|
|
||||||
@POST("api/oauth2/revoke")
|
@POST("api/oauth2/revoke")
|
||||||
Call<LogoutResponseEntity> logout(@Query("access_token") String accessToken);
|
Call<LogoutResponseEntity> logout(@Query("access_token") String accessToken);
|
||||||
|
|
||||||
//TODO sso 尚不明确
|
|
||||||
@Deprecated
|
|
||||||
@GET("api/login/sso")
|
|
||||||
Call sso(@Query("access_token") String accessToken, @Nullable @Query("gourl") String goUrl);
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
package com.hiczp.bilibili.api.passport;
|
||||||
|
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
//sso 很特别, 它可能返回的是一个 HTML 页面, 所以单独分出来
|
||||||
|
//sso 会经过两次 302 跳转, 需要保存其中的 cookie, 然后才能抵达最终页面并且进入 cookie 登录状态
|
||||||
|
public interface SsoService {
|
||||||
|
@GET("api/login/sso")
|
||||||
|
Call<ResponseBody> sso(@Nullable @Query("gourl") String goUrl);
|
||||||
|
}
|
@ -1,25 +1,28 @@
|
|||||||
package com.hiczp.bilibili.api.test;
|
package com.hiczp.bilibili.api.test;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.hiczp.bilibili.api.BilibiliAPI;
|
import com.hiczp.bilibili.api.BilibiliAPI;
|
||||||
import org.junit.Ignore;
|
import okhttp3.Cookie;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class SsoTest {
|
public class SsoTest {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(UserInfoTest.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(SsoTest.class);
|
||||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
|
||||||
private static final BilibiliAPI BILIBILI_API = Config.getBilibiliAPI();
|
private static final BilibiliAPI BILIBILI_API = Config.getBilibiliAPI();
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
// Object object = BILIBILI_API.getPassportService()
|
Map<String, List<Cookie>> cookiesMap = BILIBILI_API.toCookies();
|
||||||
// .sso(BILIBILI_API.getBilibiliAccount().getAccessToken(), null)
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
// .execute()
|
cookiesMap.forEach((domain, cookies) -> {
|
||||||
// .body();
|
stringBuilder.append("domain: ").append(domain).append("\n");
|
||||||
// LOGGER.info("{}", object.toString());
|
cookies.forEach(cookie ->
|
||||||
|
stringBuilder.append("\t").append(cookie.name()).append("=").append(cookie.value()).append("\n")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
LOGGER.info("Cookies below: \n{}", stringBuilder.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user