新版改版:

新增B站接口
测试直播WebSocket
This commit is contained in:
zlzw 2024-07-02 10:01:10 +08:00
parent b178010f8f
commit 54ac47c8b4
27 changed files with 1768 additions and 36 deletions

View File

@ -0,0 +1,184 @@
package com.yutou;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.bili.api.LiveApi;
import com.yutou.bili.api.UserApi;
import com.yutou.bili.bean.live.LiveRoomConfig;
import com.yutou.bili.bean.live.LiveRoomPlayInfo;
import com.yutou.bili.bean.login.LoginCookie;
import com.yutou.bili.bean.login.UserInfoBean;
import com.yutou.bili.enums.LiveProtocol;
import com.yutou.bili.enums.LiveVideoCodec;
import com.yutou.bili.enums.LiveVideoDefinition;
import com.yutou.bili.enums.LiveVideoFormat;
import com.yutou.bili.net.BiliLiveNetApiManager;
import com.yutou.bili.net.BiliLoginNetApiManager;
import com.yutou.bili.databases.BiliBiliLoginDatabase;
import com.yutou.bili.net.BiliUserNetApiManager;
import com.yutou.bili.net.WebSocketManager;
import com.yutou.inter.IHttpApiCheckCallback;
import com.yutou.okhttp.BaseBean;
import com.yutou.okhttp.FileCallback;
import com.yutou.okhttp.HttpCallback;
import com.yutou.okhttp.HttpLoggingInterceptor;
import jakarta.xml.bind.DatatypeConverter;
import okhttp3.Headers;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
public class Main {
public static void main(String[] args) {
HttpLoggingInterceptor.setLog(false);
HttpLoggingInterceptor.setLog(true);
getPlayUrl();
//testSocket();
}
public static void testSocket() {
try {
JSONObject json = new JSONObject();
json.put("roomid", "13246789");
json.put("protover", "3");
json.put("platform", "web");
json.put("type", 2);
json.put("key", "aaaabbb");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// outputStream.write(toLH(json.toString().length() + 16));
outputStream.write(new byte[]{0, 0, 1, 68, 0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1});
outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8));
outputStream.flush();
System.out.println("\n\n\n");
String str = DatatypeConverter.printHexBinary(outputStream.toByteArray());
for (int i = 0; i < str.length(); i = i + 4) {
if (i % 32 == 0 && i != 0) {
System.out.println();
}
if (str.length() - i > 4) {
System.out.print(str.substring(i, i + 4) + " ");
} else {
System.out.println(str.substring(i));
}
}
System.out.println("\n\n\n");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void getPlayUrl() {
BiliLiveNetApiManager
.getInstance()
.getApi(new IHttpApiCheckCallback<LiveApi>() {
@Override
public void onSuccess(LiveApi api) {
String roomId = "22689676";
String mid = "68057278";
// roomId="42062";
api.getLiveRoomPlayInfo(
roomId,
LiveProtocol.getAll(),
LiveVideoFormat.getAll(),
LiveVideoCodec.getAll(),
LiveVideoDefinition.ORIGINAL.getValue()
).enqueue(new HttpCallback<>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveRoomPlayInfo response, String rawResponse) {
LiveRoomPlayInfo.Codec codec = response.getPlayurlInfo().getPlayurl().getStream().get(0).getFormat().get(0).getCodec().get(0);
LiveRoomConfig config = new LiveRoomConfig();
config.setUid("0");
config.setRoomId(roomId);
config.setMid(mid);
config.setLogin(false);
WebSocketManager.getInstance().addRoom(config);
/*String url = codec.getUrlInfo().get(0).getHost() + codec.getBaseUrl() + codec.getUrlInfo().get(0).getExtra();
System.out.println("下载url = " + url);
api.downloadLive(url).enqueue(new FileCallback<>(response) {
@Override
public void onStart(LiveRoomPlayInfo bean) {
System.out.println("开始下载");
}
@Override
public boolean onDownload(Headers headers, LiveRoomPlayInfo bean, long len, long total) {
System.out.println("下载中:"+len+"|"+total);
return true;
}
@Override
public void onFinish(LiveRoomPlayInfo bean) {
System.out.println("下载结束");
}
@Override
public void onFailure(LiveRoomPlayInfo bean, Throwable throwable) {
System.out.println("下载失败");
}
});*/
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
@Override
public void onError(int code, String error) {
}
});
}
public static void login(String username, String password) {
BiliLoginNetApiManager.getInstance().login(new HttpCallback<LoginCookie>() {
@Override
public void onResponse(Headers headers, int code, String status, LoginCookie response, String rawResponse) {
System.out.println("headers = " + headers + ", code = " + code + ", status = " + status + ", response = " + response + ", rawResponse = " + rawResponse);
if (code == BiliLoginNetApiManager.LOGIN_SUCCESS) {
BiliBiliLoginDatabase.getInstance().initData(response).close();
}
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
public static void getUserInfo() {
BiliUserNetApiManager.getInstance().getUserApi(new IHttpApiCheckCallback<UserApi>() {
@Override
public void onSuccess(UserApi api) {
api.getUserInfo().enqueue(new HttpCallback<UserInfoBean>() {
@Override
public void onResponse(Headers headers, int code, String status, UserInfoBean response, String rawResponse) {
System.out.println("response = " + rawResponse);
System.out.println(response);
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
@Override
public void onError(int code, String error) {
System.out.println("code = " + code + ", error = " + error);
}
});
}
}

View File

@ -0,0 +1,56 @@
package com.yutou.bili.api;
import com.yutou.bili.bean.live.*;
import com.yutou.okhttp.BaseBean;
import com.yutou.okhttp.FileBody;
import com.yutou.okhttp.HttpBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Query;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
/**
* 直播间相关API
* <a href="https://socialsisteryi.github.io/bilibili-API-collect/docs/live/info.html#%E8%8E%B7%E5%8F%96%E7%9B%B4%E6%92%AD%E9%97%B4%E4%BF%A1%E6%81%AF">文档地址</a>
*/
public interface LiveApi {
/**
* 获取直播间信息
* @param roomId 直播间号 必要 可以为短号
*/
@GET("/room/v1/Room/get_info")
Call<HttpBody<LiveRoomInfo>> getRoomInfo(@Query("room_id")String roomId);
@GET("/room/v1/Room/getRoomInfoOld")
Call<HttpBody<LiveRoomStatus>> getRoomStatus(@Query("mid")String mid);
/**
* 获取主播信息
*/
@GET("/live_user/v1/Master/info")
Call<HttpBody<MasterInfoBean>> getMasterInfo(@Query("uid")String uid);
/**
* 获取直播间信息
* @param id 直播间id
* @param protocol 直播协议 {@link com.yutou.bili.enums.LiveProtocol}
* @param format 格式 {@link com.yutou.bili.enums.LiveVideoFormat}
* @param codec 编码 {@link com.yutou.bili.enums.LiveVideoCodec}
* @param qn 清晰度 {@link com.yutou.bili.enums.LiveVideoDefinition}
* @return
*/
@GET("/xlive/web-room/v2/index/getRoomPlayInfo")
Call<HttpBody<LiveRoomPlayInfo>> getLiveRoomPlayInfo(
@Query("room_id")String id,
@Query("protocol")String protocol,
@Query("format")String format,
@Query("codec")String codec,
@Query("qn")int qn
);
@Streaming
@GET()
Call<FileBody<LiveRoomPlayInfo>> downloadLive(@Url String url);
@GET("/xlive/web-room/v1/index/getDanmuInfo")
Call<HttpBody<LiveDanmuInfo>> getLiveRoomDanmuInfo(@Query("id")String id);
}

View File

@ -1,5 +1,6 @@
package com.yutou.bili.api; package com.yutou.bili.api;
import com.yutou.bili.bean.login.CheckCookieBean;
import com.yutou.bili.bean.login.LoginInfoBean; import com.yutou.bili.bean.login.LoginInfoBean;
import com.yutou.bili.bean.login.QRCodeGenerateBean; import com.yutou.bili.bean.login.QRCodeGenerateBean;
import com.yutou.okhttp.HttpBody; import com.yutou.okhttp.HttpBody;
@ -8,11 +9,23 @@ import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;
public interface LoginApi { public interface LoginApi {
/**
* 获取登陆二维码
*/
@GET("/x/passport-login/web/qrcode/generate") @GET("/x/passport-login/web/qrcode/generate")
Call<HttpBody<QRCodeGenerateBean>> getQRCodeGenerate(); Call<HttpBody<QRCodeGenerateBean>> getQRCodeGenerate();
/**
* 通过二维码登录
*/
@GET("/x/passport-login/web/qrcode/poll") @GET("/x/passport-login/web/qrcode/poll")
Call<HttpBody<LoginInfoBean>> loginQRCode(@Query("qrcode_key") String qrcode_key); Call<HttpBody<LoginInfoBean>> loginQRCode(@Query("qrcode_key") String qrcode_key);
/**
* 检查cookie是否有效太麻烦了以后做
*/
@GET("x/passport-login/web/cookie/info")
Call<HttpBody<CheckCookieBean>> checkCookie();
} }

View File

@ -0,0 +1,46 @@
package com.yutou.bili.bean.live;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
@Data
public class LiveDanmuInfo {
@JSONField(name = "group")
private String group;
@JSONField(name = "business_id")
private int businessId;
@JSONField(name = "refresh_row_factor")
private double refreshRowFactor;
@JSONField(name = "refresh_rate")
private int refreshRate;
@JSONField(name = "max_delay")
private int maxDelay;
@JSONField(name = "token")
private String token;
@JSONField(name = "host_list")
private List<Host> hostList;
@Data
public static class Host {
@JSONField(name = "host")
private String host;
@JSONField(name = "port")
private int port;
@JSONField(name = "wss_port")
private int wssPort;
@JSONField(name = "ws_port")
private int wsPort;
}
}

View File

@ -0,0 +1,13 @@
package com.yutou.bili.bean.live;
import lombok.Data;
@Data
public class LiveRoomConfig {
String uid;
String roomId;
String mid;//真实房间id
boolean isLogin;
LiveDanmuInfo liveInfo;
}

View File

@ -0,0 +1,234 @@
package com.yutou.bili.bean.live;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
@Data
public class LiveRoomInfo {
@JSONField(name = "uid")
private int uid;
@JSONField(name = "room_id")
private int roomId;
@JSONField(name = "short_id")
private int shortId;
@JSONField(name = "attention")
private int attention;
@JSONField(name = "online")
private int online;
@JSONField(name = "is_portrait")
private boolean isPortrait;
@JSONField(name = "description")
private String description;
@JSONField(name = "live_status")
private int liveStatus;
@JSONField(name = "area_id")
private int areaId;
@JSONField(name = "parent_area_id")
private int parentAreaId;
@JSONField(name = "parent_area_name")
private String parentAreaName;
@JSONField(name = "old_area_id")
private int oldAreaId;
@JSONField(name = "background")
private String background;
@JSONField(name = "title")
private String title;
@JSONField(name = "user_cover")
private String userCover;
@JSONField(name = "keyframe")
private String keyframe;
@JSONField(name = "is_strict_room")
private boolean isStrictRoom;
@JSONField(name = "live_time")
private String liveTime;
@JSONField(name = "tags")
private String tags;
@JSONField(name = "is_anchor")
private int isAnchor;
@JSONField(name = "room_silent_type")
private String roomSilentType;
@JSONField(name = "room_silent_level")
private int roomSilentLevel;
@JSONField(name = "room_silent_second")
private int roomSilentSecond;
@JSONField(name = "area_name")
private String areaName;
@JSONField(name = "pendants")
private String pendants;
@JSONField(name = "area_pendants")
private String areaPendants;
@JSONField(name = "hot_words")
private List<String> hotWords;
@JSONField(name = "hot_words_status")
private int hotWordsStatus;
@JSONField(name = "verify")
private String verify;
@JSONField(name = "new_pendants")
private NewPendants newPendants;
@JSONField(name = "up_session")
private String upSession;
@JSONField(name = "pk_status")
private int pkStatus;
@JSONField(name = "pk_id")
private int pkId;
@JSONField(name = "battle_id")
private int battleId;
@JSONField(name = "allow_change_area_time")
private int allowChangeAreaTime;
@JSONField(name = "allow_upload_cover_time")
private int allowUploadCoverTime;
@JSONField(name = "studio_info")
private StudioInfo studioInfo;
@Data
public static class NewPendants {
@JSONField(name = "frame")
private Frame frame;
@JSONField(name = "badge")
private Badge badge;
@JSONField(name = "mobile_frame")
private MobileFrame mobileFrame;
@JSONField(name = "mobile_badge")
private MobileBadge mobileBadge;
}
@Data
public static class Frame {
@JSONField(name = "name")
private String name;
@JSONField(name = "value")
private String value;
@JSONField(name = "position")
private int position;
@JSONField(name = "desc")
private String desc;
@JSONField(name = "area")
private int area;
@JSONField(name = "area_old")
private int areaOld;
@JSONField(name = "bg_color")
private String bgColor;
@JSONField(name = "bg_pic")
private String bgPic;
@JSONField(name = "use_old_area")
private boolean useOldArea;
}
@Data
public static class Badge {
@JSONField(name = "name")
private String name;
@JSONField(name = "position")
private int position;
@JSONField(name = "value")
private String value;
@JSONField(name = "desc")
private String desc;
}
@Data
public static class MobileFrame {
@JSONField(name = "name")
private String name;
@JSONField(name = "value")
private String value;
@JSONField(name = "position")
private int position;
@JSONField(name = "desc")
private String desc;
@JSONField(name = "area")
private int area;
@JSONField(name = "area_old")
private int areaOld;
@JSONField(name = "bg_color")
private String bgColor;
@JSONField(name = "bg_pic")
private String bgPic;
@JSONField(name = "use_old_area")
private boolean useOldArea;
}
@Data
public static class MobileBadge {
@JSONField(name = "name")
private String name;
@JSONField(name = "position")
private int position;
@JSONField(name = "value")
private String value;
@JSONField(name = "desc")
private String desc;
}
@Data
public static class StudioInfo {
@JSONField(name = "status")
private int status;
@JSONField(name = "master_list")
private List<String> masterList;
}
}

View File

@ -0,0 +1,231 @@
package com.yutou.bili.bean.live;
import com.alibaba.fastjson2.annotation.JSONField;
import com.yutou.okhttp.BaseBean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class LiveRoomPlayInfo extends BaseBean {
@JSONField(name = "room_id")
private int roomId;
@JSONField(name = "short_id")
private int shortId;
@JSONField(name = "uid")
private int uid;
@JSONField(name = "is_hidden")
private boolean isHidden;
@JSONField(name = "is_locked")
private boolean isLocked;
@JSONField(name = "is_portrait")
private boolean isPortrait;
@JSONField(name = "live_status")
private int liveStatus;
@JSONField(name = "hidden_till")
private int hiddenTill;
@JSONField(name = "lock_till")
private int lockTill;
@JSONField(name = "encrypted")
private boolean encrypted;
@JSONField(name = "pwd_verified")
private boolean pwdVerified;
@JSONField(name = "live_time")
private int liveTime;
@JSONField(name = "room_shield")
private int roomShield;
@JSONField(name = "all_special_types")
private List<Integer> allSpecialTypes;
@JSONField(name = "playurl_info")
private PlayurlInfo playurlInfo;
@JSONField(name = "official_type")
private int officialType;
@JSONField(name = "official_room_id")
private int officialRoomId;
@JSONField(name = "risk_with_delay")
private int riskWithDelay;
@Data
public static class PlayurlInfo {
@JSONField(name = "conf_json")
private String confJson;
@JSONField(name = "playurl")
private Playurl playurl;
@JSONField(name = "dolby_qn")
private Integer dolbyQn;
}
@Data
public static class Playurl {
@JSONField(name = "cid")
private int cid;
@JSONField(name = "g_qn_desc")
private List<QnDesc> gQnDesc;
@JSONField(name = "stream")
private List<Stream> stream;
@JSONField(name = "p2p_data")
private P2pData p2pData;
}
@Data
public static class QnDesc {
@JSONField(name = "qn")
private int qn;
@JSONField(name = "desc")
private String desc;
@JSONField(name = "hdr_desc")
private String hdrDesc;
@JSONField(name = "attr_desc")
private Object attrDesc;
}
@Data
public static class Stream {
@JSONField(name = "protocol_name")
private String protocolName;
@JSONField(name = "format")
private List<Format> format;
@JSONField(name = "p2p_type")
private int p2pType;
@JSONField(name = "free_type")
private int freeType;
@JSONField(name = "mid")
private int mid;
@JSONField(name = "sid")
private String sid;
@JSONField(name = "chash")
private int chash;
@JSONField(name = "bmt")
private int bmt;
@JSONField(name = "sche")
private String sche;
@JSONField(name = "score")
private int score;
@JSONField(name = "pp")
private String pp;
@JSONField(name = "source")
private String source;
@JSONField(name = "trace")
private int trace;
@JSONField(name = "site")
private String site;
@JSONField(name = "zoneid_l")
private int zoneidL;
@JSONField(name = "sid_l")
private String sidL;
@JSONField(name = "order")
private int order;
}
@Data
public static class Format {
@JSONField(name = "format_name")
private String formatName;
@JSONField(name = "codec")
private List<Codec> codec;
@JSONField(name = "hdr_qn")
private Integer hdrQn;
@JSONField(name = "dolby_type")
private int dolbyType;
@JSONField(name = "attr_name")
private String attrName;
}
@Data
public static class Codec {
@JSONField(name = "codec_name")
private String codecName;
@JSONField(name = "current_qn")
private int currentQn;
@JSONField(name = "accept_qn")
private List<Integer> acceptQn;
@JSONField(name = "base_url")
private String baseUrl;
@JSONField(name = "url_info")
private List<UrlInfo> urlInfo;
@JSONField(name = "hdr_desc")
private String hdrDesc;
@JSONField(name = "attr_desc")
private Object attrDesc;
}
@Data
public static class UrlInfo {
@JSONField(name = "host")
private String host;
@JSONField(name = "extra")
private String extra;
@JSONField(name = "stream_ttl")
private int streamTtl;
}
@Data
public static class P2pData {
@JSONField(name = "p2p")
private boolean p2p;
@JSONField(name = "p2p_type")
private int p2pType;
@JSONField(name = "m_p2p")
private boolean mP2p;
@JSONField(name = "m_servers")
private List<String> mServers;
}
}

View File

@ -0,0 +1,37 @@
package com.yutou.bili.bean.live;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
@Data
public class LiveRoomStatus {
@JSONField(name = "roomStatus")
private int roomStatus;
@JSONField(name = "roundStatus")
private int roundStatus;
@JSONField(name = "liveStatus")
private int liveStatus;
@JSONField(name = "url")
private String url;
@JSONField(name = "title")
private String title;
@JSONField(name = "cover")
private String cover;
@JSONField(name = "online")
private int online;
@JSONField(name = "roomid")
private int roomid;
@JSONField(name = "broadcast_type")
private int broadcastType;
@JSONField(name = "online_hidden")
private int onlineHidden;
}

View File

@ -0,0 +1,99 @@
package com.yutou.bili.bean.live;
import com.alibaba.fastjson2.annotation.JSONField;
import com.yutou.okhttp.BaseBean;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class MasterInfoBean extends BaseBean {
@JSONField(name = "info")
private Info info;
@JSONField(name = "exp")
private Exp exp;
@JSONField(name = "follower_num")
private int followerNum;
@JSONField(name = "room_id")
private int roomId;
@JSONField(name = "medal_name")
private String medalName;
@JSONField(name = "glory_count")
private int gloryCount;
@JSONField(name = "pendant")
private String pendant;
@JSONField(name = "link_group_num")
private int linkGroupNum;
@JSONField(name = "room_news")
private RoomNews roomNews;
@Data
public static class Info {
@JSONField(name = "uid")
private int uid;
@JSONField(name = "uname")
private String uname;
@JSONField(name = "face")
private String face;
@JSONField(name = "official_verify")
private OfficialVerify officialVerify;
@JSONField(name = "gender")
private int gender;
}
@Data
public static class Exp {
@JSONField(name = "master_level")
private MasterLevel masterLevel;
}
@Data
public static class MasterLevel {
@JSONField(name = "level")
private int level;
@JSONField(name = "color")
private int color;
@JSONField(name = "current")
private List<Integer> current;
@JSONField(name = "next")
private List<Integer> next;
}
@Data
public static class OfficialVerify {
@JSONField(name = "type")
private int type;
@JSONField(name = "desc")
private String desc;
}
@Data
public static class RoomNews {
@JSONField(name = "content")
private String content;
@JSONField(name = "ctime")
private String ctime;
@JSONField(name = "ctime_text")
private String ctimeText;
}
}

View File

@ -0,0 +1,15 @@
package com.yutou.bili.bean.login;
import com.alibaba.fastjson2.annotation.JSONField;
import com.yutou.okhttp.BaseBean;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class CheckCookieBean extends BaseBean {
@JSONField(name = "refresh")
boolean refresh;
@JSONField(name = "timestamp")
long timestamp;
}

View File

@ -35,12 +35,16 @@ public class BiliBiliLoginDatabase extends SQLiteManager {
return this; return this;
} }
public List<LoginCookie> get() { public LoginCookie get() {
return super.get(cookie.getTableName(), LoginCookie.class); List<LoginCookie> list = super.get(cookie.getTableName(), LoginCookie.class);
if (!list.isEmpty()) {
return list.get(0);
}
return null;
} }
@Override @Override
protected LoginCookie getDataBean() { protected LoginCookie getDataBean() {
return new LoginCookie(); return new LoginCookie();
} }
} }

View File

@ -0,0 +1,23 @@
package com.yutou.bili.enums;
import lombok.Getter;
@Getter
public enum LiveProtocol {
stream(0),
hls(1);
private final int value;
private LiveProtocol(int value) {
this.value = value;
}
public static String getAll() {
StringBuilder sb = new StringBuilder();
for (LiveProtocol value : values()) {
sb.append(String.valueOf(value.value)).append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -0,0 +1,22 @@
package com.yutou.bili.enums;
import lombok.Getter;
@Getter
public enum LiveVideoCodec {
AVC(0),
HEVC(1);
private final int value;
private LiveVideoCodec(int value) {
this.value=value;
}
public static String getAll() {
StringBuilder sb = new StringBuilder();
for (LiveVideoCodec value : values()) {
sb.append(String.valueOf(value.value)).append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -0,0 +1,28 @@
package com.yutou.bili.enums;
import lombok.Getter;
@Getter
public enum LiveVideoDefinition {
LOW(80), // 流畅
HIGH(150), // 高清
SUPER(250), // 超清
BLU_RAY(400), // 蓝光
ORIGINAL(10000), // 原画
V4K(20000),
DOLBY(30000);
private final int value;
private LiveVideoDefinition(int value) {
this.value = value;
}
public static String getAll() {
StringBuilder sb = new StringBuilder();
for (LiveVideoDefinition value : values()) {
sb.append(String.valueOf(value.value)).append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -0,0 +1,23 @@
package com.yutou.bili.enums;
import lombok.Getter;
@Getter
public enum LiveVideoFormat {
FLV(0),
TS(1),
FMP4(2);
private final int value;
private LiveVideoFormat(int value) {
this.value=value;
}
public static String getAll() {
StringBuilder sb = new StringBuilder();
for (LiveVideoFormat value : values()) {
sb.append(String.valueOf(value.value)).append(",");
}
return sb.substring(0, sb.length() - 1);
}
}

View File

@ -0,0 +1,90 @@
package com.yutou.bili.net;
import com.yutou.bili.bean.login.CheckCookieBean;
import com.yutou.bili.bean.login.LoginInfoBean;
import com.yutou.inter.IHttpApiCheckCallback;
import com.yutou.okhttp.HttpCallback;
import com.yutou.utils.RSAUtils;
import okhttp3.Headers;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class BiliCookieManager {
public static final int COOKIE_INVALID = -101;
public static final int COOKIE_SUCCESS = 0;
private static final String PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\n" +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLgd2OAkcGVtoE3ThUREbio0Eg\n" +
"Uc/prcajMKXvkCKFCWhJYJcLkcM2DKKcSeFpD/j6Boy538YXnR6VhcuUJOhH2x71\n" +
"nzPjfdTcqMz7djHum0qSZA0AyCBDABUqCrfNgCiJ00Ra7GmRj+YCK1NJEuewlb40\n" +
"JNrRuoEUXpabUzGB8QIDAQAB\n" +
"-----END PUBLIC KEY-----";
public void checkCookie(IHttpApiCheckCallback<Integer> callback){
BiliLoginNetApiManager.getInstance().getLoginApi(true)
.checkCookie().enqueue(new HttpCallback<CheckCookieBean>() {
@Override
public void onResponse(Headers headers, int code, String status, CheckCookieBean response, String rawResponse) {
if(code==-101){
// TODO cookie失效需要重新登录
callback.onError(COOKIE_INVALID,"cookie失效需要重新登录");
return;
}
if(response.isRefresh()){
refreshCookie();
}
callback.onSuccess(COOKIE_SUCCESS);
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
/**
* <a href="https://socialsisteryi.github.io/bilibili-API-collect/docs/login/cookie_refresh.html#java">文档地址</a>
*/
private void refreshCookie(){
try {
String refreshTime = String.format("refresh_%d", System.currentTimeMillis());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
String publicKeyStr = PUBLIC_KEY
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replace("\n", "")
.trim();
byte[] publicBytes = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
String algorithm = "RSA/ECB/OAEPPadding";
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
// Encode the plaintext to bytes
byte[] plaintextBytes = refreshTime.getBytes(StandardCharsets.UTF_8);
// Add OAEP padding to the plaintext bytes
OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
// Encrypt the padded plaintext bytes
byte[] encryptedBytes = cipher.doFinal(plaintextBytes);
// Convert the encrypted bytes to a Base64-encoded string
String encrypted = new BigInteger(1, encryptedBytes).toString(16);
}catch (Exception e){
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,39 @@
package com.yutou.bili.net;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.bili.api.LiveApi;
import com.yutou.bili.bean.login.LoginCookie;
import com.yutou.bili.databases.BiliBiliLoginDatabase;
import com.yutou.inter.IHttpApiCheckCallback;
import com.yutou.okhttp.api.BaseApi;
import java.util.HashMap;
public class BiliLiveNetApiManager extends BaseApi {
private static BiliLiveNetApiManager instance;
public static BiliLiveNetApiManager getInstance() {
if (instance == null) {
instance = new BiliLiveNetApiManager("https://api.live.bilibili.com");
}
return instance;
}
private BiliLiveNetApiManager(String URL) {
super(URL);
}
public void getApi(IHttpApiCheckCallback<LiveApi> callback) {
LoginCookie cookie = BiliBiliLoginDatabase.getInstance().get();
if (cookie != null) {
useCookie(JSONObject.parseObject(JSONObject.toJSONString(cookie)));
}
HashMap<String,String> header=new HashMap<>();
header.put("Accept-Language", "zh-CN,zh;q=0.8");
header.put("Referer", "https://live.bilibili.com");
header.put("Connection", "keep-alive");
header.put("Upgrade-Insecure-Requests", "1");
setHeaders(header);
callback.onSuccess(createApi(LiveApi.class));
}
}

View File

@ -6,7 +6,6 @@ import com.yutou.bili.bean.login.LoginCookie;
import com.yutou.bili.bean.login.LoginInfoBean; import com.yutou.bili.bean.login.LoginInfoBean;
import com.yutou.bili.bean.login.QRCodeGenerateBean; import com.yutou.bili.bean.login.QRCodeGenerateBean;
import com.yutou.bili.databases.BiliBiliLoginDatabase; import com.yutou.bili.databases.BiliBiliLoginDatabase;
import com.yutou.databases.AbsDatabasesBean;
import com.yutou.okhttp.HttpBody; import com.yutou.okhttp.HttpBody;
import com.yutou.okhttp.HttpCallback; import com.yutou.okhttp.HttpCallback;
import com.yutou.okhttp.api.BaseApi; import com.yutou.okhttp.api.BaseApi;
@ -38,10 +37,24 @@ public class BiliLoginNetApiManager extends BaseApi {
return instance; return instance;
} }
public LoginApi getLoginApi() {
return getLoginApi(false);
}
public LoginApi getLoginApi(boolean isCookie) {
if (isCookie) {
LoginCookie cookie = BiliBiliLoginDatabase.getInstance().get();
if (cookie != null) {
useCookie(JSONObject.parseObject(JSONObject.toJSONString(cookie)));
}
}
return loginApi;
}
public void login(HttpCallback<LoginCookie> callback) { public void login(HttpCallback<LoginCookie> callback) {
List<LoginCookie> cookie = BiliBiliLoginDatabase.getInstance().get(); LoginCookie cookie = BiliBiliLoginDatabase.getInstance().get();
if (!cookie.isEmpty()) { if (cookie != null) {
callback.onResponse(null, LOGIN_SUCCESS, null, cookie.get(0), null); callback.onResponse(null, LOGIN_SUCCESS, null, cookie, null);
return; return;
} }
loginApi.getQRCodeGenerate().enqueue(new HttpCallback<QRCodeGenerateBean>() { loginApi.getQRCodeGenerate().enqueue(new HttpCallback<QRCodeGenerateBean>() {

View File

@ -27,12 +27,11 @@ public class BiliUserNetApiManager extends BaseApi {
} }
public void getUserApi(IHttpApiCheckCallback<UserApi> callback) { public void getUserApi(IHttpApiCheckCallback<UserApi> callback) {
List<LoginCookie> list = BiliBiliLoginDatabase.getInstance().get(); LoginCookie cookie = BiliBiliLoginDatabase.getInstance().get();
if (!list.isEmpty()) { if (cookie != null) {
HashMap<String, String> headers = new HashMap<>(); HashMap<String, String> headers = new HashMap<>();
LoginCookie cookie = list.get(0);
JSONObject json = JSONObject.parseObject(JSONObject.toJSONString(cookie)); JSONObject json = JSONObject.parseObject(JSONObject.toJSONString(cookie));
StringBuilder ck=new StringBuilder(); StringBuilder ck = new StringBuilder();
for (String key : json.keySet()) { for (String key : json.keySet()) {
ck.append(key).append("=").append(json.getString(key)).append("; "); ck.append(key).append("=").append(json.getString(key)).append("; ");
} }

View File

@ -0,0 +1,230 @@
package com.yutou.bili.net;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.bili.api.LiveApi;
import com.yutou.bili.bean.live.LiveDanmuInfo;
import com.yutou.bili.bean.live.LiveRoomConfig;
import com.yutou.inter.IHttpApiCheckCallback;
import com.yutou.okhttp.HttpCallback;
import jakarta.xml.bind.DatatypeConverter;
import okhttp3.Headers;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.io.ByteArrayOutputStream;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.Inflater;
public class WebSocketManager {
private static WebSocketManager instance;
Map<LiveRoomConfig, WebSocketClientTh> roomMap;
private WebSocketManager() {
roomMap = new HashMap<>();
}
public static WebSocketManager getInstance() {
if (instance == null) {
instance = new WebSocketManager();
}
return instance;
}
public void addRoom(LiveRoomConfig roomConfig) {
BiliLiveNetApiManager.getInstance().getApi(new IHttpApiCheckCallback<LiveApi>() {
@Override
public void onSuccess(LiveApi api) {
api.getLiveRoomDanmuInfo(roomConfig.getRoomId()).enqueue(new HttpCallback<LiveDanmuInfo>() {
@Override
public void onResponse(Headers headers, int code, String status, LiveDanmuInfo response, String rawResponse) {
if (!response.getHostList().isEmpty()) {
LiveDanmuInfo.Host host = response.getHostList().get(0);
String url = "wss://" + host.getHost() + ":" + host.getWssPort() + "/sub";
// url="ws://127.0.0.1:8765";
try {
roomConfig.setLiveInfo(response);
new WebSocketClientTh(new URI(url), roomConfig);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
}
@Override
public void onFailure(Throwable throwable) {
throwable.printStackTrace();
}
});
}
@Override
public void onError(int code, String error) {
}
});
}
private static class WebSocketClientTh extends WebSocketClient {
private LiveRoomConfig roomConfig;
private HeartbeatTask heartbeatTask;
public WebSocketClientTh(URI serverUri, LiveRoomConfig roomId) {
super(serverUri);
this.roomConfig = roomId;
heartbeatTask = new HeartbeatTask();
HashMap<String, String> header = new HashMap<>();
header.put("Sec-WebSocket-Extensions", "permessage-deflate; client_max_window_bits");
header.put("Sec-WebSocket-Key", "f1kFoce72Pn+wbjdPyONLw==");
// header.put("Sec-WebSocket-Key", roomId.getLiveInfo().getToken());
header.put("Sec-WebSocket-Version", "13");
header.put("Cache-Control", "no-cache");
header.put("Connection", "Upgrade");
header.put("Accept-Encoding:", "gzip, deflate, br, zstd");
header.put("Host", roomId.getLiveInfo().getHostList().get(0).getHost() + ":" + roomId.getLiveInfo().getHostList().get(0).getWssPort());
header.put("Origin", "https://live.bilibili.com");
header.put("Pragma", "no-cache");
header.put("Upgrade", "websocket");
header.put("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36");
for (String key : header.keySet()) {
addHeader(key, header.get(key));
}
System.out.println();
System.out.println();
System.out.println("header = " + header);
connect();
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
WebSocketManager.getInstance().roomMap.put(roomConfig, this);
heartbeatTask.setSocket(this);
heartbeatTask.sendInitAuthData();
new Timer().schedule(heartbeatTask, 0, 30000);
System.out.println("WebSocketClientTh.onOpen");
}
@Override
public void onMessage(String s) {
System.out.println("s = " + s);
}
@Override
public void onMessage(ByteBuffer bytes) {
System.out.println("WebSocketClientTh.onMessage");
super.onMessage(bytes);
decompress(bytes.array());
}
@Override
public void onClose(int i, String s, boolean b) {
System.out.println("WebSocketClientTh.onClose");
System.out.println("i = " + i + ", s = " + s + ", b = " + b);
WebSocketManager.getInstance().roomMap.remove(roomConfig);
heartbeatTask.cancel();
}
@Override
public void onError(Exception e) {
System.out.println("WebSocketClientTh.onError");
e.printStackTrace();
WebSocketManager.getInstance().roomMap.remove(roomConfig);
heartbeatTask.cancel();
}
/**
* 解压缩
*
* @param data 待压缩的数据
*/
public void decompress(byte[] data) {
try {
Inflater inflater = new Inflater();
inflater.reset();
inflater.setInput(data);
ByteArrayOutputStream out = new ByteArrayOutputStream(data.length);
byte[] buf = new byte[8192];
while (!inflater.finished()) {
int i = inflater.inflate(buf);
out.write(buf, 0, i);
}
System.out.println("接收值:" + new String(out.toByteArray()));
} catch (Exception e) {
e.printStackTrace();
}
}
private class HeartbeatTask extends TimerTask {
WebSocketClientTh socket;
public void setSocket(WebSocketClientTh socket) {
this.socket = socket;
}
@Override
public void run() {
}
public void sendInitAuthData() {
JSONObject json = new JSONObject();
if (roomConfig.isLogin()) {
json.put("uid", roomConfig.getUid());
} else {
json.put("uid", "0");
}
try {
json.put("roomid", roomConfig.getRoomId());
json.put("protover", "3");
json.put("platform", "web");
json.put("buvid", "F56FA00C-B043-DDB7-B7D2-D1A2AEC3034E24804infoc");
json.put("type", 2);
json.put("key", roomConfig.getLiveInfo().getToken());
byte[] bytes = {0, 16, 0, 1, 0, 0, 0, 7, 0, 0, 0, 1};
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(toLH(json.toString().length() + bytes.length));
outputStream.write(bytes);
outputStream.write(json.toJSONString().getBytes(StandardCharsets.UTF_8));
outputStream.flush();
System.out.println("\n\n\n");
String str = DatatypeConverter.printHexBinary(outputStream.toByteArray());
for (int i = 0; i < str.length(); i = i + 4) {
if (i % 32 == 0 && i != 0) {
System.out.println();
}
if (str.length() - i > 4) {
System.out.print(str.substring(i, i + 4) + " ");
} else {
System.out.println(str.substring(i));
}
}
System.out.println("\n\n\n");
System.out.println(socket.isOpen());
socket.send(outputStream.toByteArray());
} catch (Exception e) {
e.printStackTrace();
}
}
public byte[] toLH(int n) {
byte[] b = new byte[4];
b[3] = (byte) (n & 0xff);
b[2] = (byte) (n >> 8 & 0xff);
b[1] = (byte) (n >> 16 & 0xff);
b[0] = (byte) (n >> 24 & 0xff);
return b;
}
}
}
}

View File

@ -0,0 +1,69 @@
package com.yutou.bili.utils;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LiveHeartBeat {
private static LiveHeartBeat instance;
private HeartBeatThread thread;
Map<String,HeartBeatThread> heartBeatMap=new HashMap<>();
public static LiveHeartBeat getInstance() {
if (instance == null) {
instance = new LiveHeartBeat();
}
return instance;
}
private LiveHeartBeat() {
}
public void addHeartBeat(String roomId) {
if(heartBeatMap.containsKey(roomId)){
return;
}
}
public void removeHeartBeat(String roomId) {
}
public void endAllHeartBeat() {
}
public class HeartBeatThread extends WebSocketClient {
public HeartBeatThread(URI serverUri) {
super(serverUri);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
}
@Override
public void onMessage(String s) {
}
@Override
public void onClose(int i, String s, boolean b) {
}
@Override
public void onError(Exception e) {
}
}
}

View File

@ -0,0 +1,11 @@
package com.yutou.okhttp;
import lombok.Data;
import java.io.InputStream;
@Data
public class FileBody<T> {
InputStream inputStream;
T t;
}

View File

@ -0,0 +1,103 @@
package com.yutou.okhttp;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public abstract class FileCallback<T> implements Callback<FileBody<T>> {
private static ThreadPoolExecutor executor;
private final T bean;
public FileCallback(T bean) {
this.bean = bean;
if (executor == null) {
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
}
private class DownloadTask implements Runnable {
private T bean;
private Headers headers;
private HttpUrl url;
private InputStream inputStream;
public DownloadTask(T bean, Headers headers, HttpUrl url, InputStream inputStream) {
this.bean = bean;
this.headers = headers;
this.url = url;
this.inputStream = inputStream;
}
@Override
public void run() {
try {
System.out.println("开始下载");
File file = new File("download" + File.separator + System.currentTimeMillis() + ".flv");
onStart(bean);
if (!file.exists()) {
boolean mkdirs = file.getParentFile().mkdirs();
System.out.println("mkdirs = " + mkdirs);
}
FileOutputStream outputStream = new FileOutputStream(file);
int len;
long total = 0;
byte[] bytes = new byte[4096];
boolean isDownload = true;
long available = inputStream.available();
while ((len = inputStream.read(bytes)) != -1 && isDownload) {
total += len;
outputStream.write(bytes, 0, len);
outputStream.flush();
isDownload = onDownload(headers, bean, total, available);
}
System.out.println("下载完成");
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
onFinish(bean);
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public abstract void onStart(T bean);
public abstract boolean onDownload(Headers headers, T bean, long len, long total);
public abstract void onFinish(T bean);
public abstract void onFailure(T bean, Throwable throwable);
@Override
public void onResponse(Call<FileBody<T>> call, Response<FileBody<T>> response) {
executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream()));
}
@Override
public void onFailure(Call<FileBody<T>> call, Throwable throwable) {
onFailure(bean, throwable);
call.cancel();
}
}

View File

@ -38,6 +38,7 @@ public class ParamsContext {
break; break;
} }
headerMap.remove("tableName"); headerMap.remove("tableName");
headerMap.put("User-Agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36");
return iRequestParam.getRequest(headerMap, map, request); return iRequestParam.getRequest(headerMap, map, request);
} }
} }

View File

@ -1,5 +1,6 @@
package com.yutou.okhttp.api; package com.yutou.okhttp.api;
import com.alibaba.fastjson2.JSONObject;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.internal.bind.DateTypeAdapter; import com.google.gson.internal.bind.DateTypeAdapter;
@ -7,10 +8,7 @@ import com.yutou.okhttp.HttpLoggingInterceptor;
import com.yutou.okhttp.ParamsContext; import com.yutou.okhttp.ParamsContext;
import com.yutou.okhttp.converter.JsonCallAdapter; import com.yutou.okhttp.converter.JsonCallAdapter;
import com.yutou.okhttp.converter.JsonConverterFactory; import com.yutou.okhttp.converter.JsonConverterFactory;
import okhttp3.Interceptor; import okhttp3.*;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.CallAdapter; import retrofit2.CallAdapter;
import retrofit2.Converter; import retrofit2.Converter;
import retrofit2.Retrofit; import retrofit2.Retrofit;
@ -21,8 +19,8 @@ import java.util.HashMap;
public class BaseApi { public class BaseApi {
private String URL; private String URL;
private HashMap<String, String> params; private HashMap<String, String> params = new HashMap<>();
private HashMap<String, String> headers; private HashMap<String, String> headers = new HashMap<>();
public BaseApi(String URL) { public BaseApi(String URL) {
this.URL = URL; this.URL = URL;
@ -42,16 +40,24 @@ public class BaseApi {
this.params = params; this.params = params;
return this; return this;
} }
public void useCookie(JSONObject json){
StringBuilder ck = new StringBuilder();
for (String key : json.keySet()) {
ck.append(key).append("=").append(json.getString(key)).append("; ");
}
headers.put("Cookie", ck.toString());
setHeaders(headers);
}
/** /**
* 创建一个接口方法 * 创建一个接口方法
* *
* @param okHttpClient okhttp客户端 * @param okHttpClient okhttp客户端
* @param converterFactory 处理工厂类 * @param converterFactory 处理工厂类
* @param callAdapterFactory 请求适配器工厂 * @param callAdapterFactory 请求适配器工厂
* @param baseUrl 基础地质 * @param baseUrl 基础地质
* @param service 接口 * @param service 接口
* @param <T> 接口泛型 * @param <T> 接口泛型
* @return 接口 * @return 接口
*/ */
public <T> T create(OkHttpClient okHttpClient, Converter.Factory converterFactory, CallAdapter.Factory callAdapterFactory, String baseUrl, Class<T> service) { public <T> T create(OkHttpClient okHttpClient, Converter.Factory converterFactory, CallAdapter.Factory callAdapterFactory, String baseUrl, Class<T> service) {
@ -92,13 +98,14 @@ public class BaseApi {
URL, URL,
apiClass); apiClass);
} }
public Interceptor initQuery() { public Interceptor initQuery() {
Interceptor addQueryParameterInterceptor = new Interceptor() { Interceptor addQueryParameterInterceptor = new Interceptor() {
@Override @Override
public Response intercept(Chain chain) throws IOException { public Response intercept(Chain chain) throws IOException {
Request request = chain.request(); Request request = chain.request();
//配置公共参数 //配置公共参数
request = new ParamsContext(headers,params,request).getInRequest(); request = new ParamsContext(headers, params, request).getInRequest();
return chain.proceed(request); return chain.proceed(request);
} }
}; };

View File

@ -2,9 +2,9 @@ package com.yutou.okhttp.converter;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.yutou.okhttp.FileBody;
import com.yutou.okhttp.HttpBody; import com.yutou.okhttp.HttpBody;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -27,19 +27,25 @@ public class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T>
@Nullable @Nullable
@Override @Override
public T convert(ResponseBody responseBody) throws IOException { public T convert(ResponseBody responseBody) throws IOException {
String string = new String(responseBody.bytes()); if (type.getTypeName().contains(HttpBody.class.getSimpleName())) {
responseBody.close(); String string = new String(responseBody.bytes());
HttpBody<T> body; responseBody.close();
try { HttpBody<T> body;
body = JSONObject.parseObject(string, type, JSONReader.Feature.FieldBased); try {
body.setSrc(string); body = JSONObject.parseObject(string, type, JSONReader.Feature.FieldBased);
body.setSrc(string);
return (T) body;
} catch (Exception e) {
e.printStackTrace();
body = new HttpBody();
body.setSrc(string);
}
return (T) body; return (T) body;
} catch (Exception e) {
e.printStackTrace();
body = new HttpBody();
body.setSrc(string);
}
return (T) body;
} else {
FileBody<T> body=new FileBody<T>();
body.setInputStream(responseBody.byteStream());
return (T) body;
}
} }
} }

View File

@ -0,0 +1,136 @@
package com.yutou.utils;
import org.apache.commons.codec.binary.Base64;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
public class RSAUtils {
/**
* 加密算法RSA
*/
public static final String KEY_ALGORITHM = "RSA";
/**
* 签名算法
*/
public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
/**
* 获取公钥的key
*/
private static final String PUBLIC_KEY = "RSAPublicKey";
/**
* 获取私钥的key
*/
private static final String PRIVATE_KEY = "RSAPrivateKey";
/**
* RSA 密钥位数
*/
private static final int KEY_SIZE = 1024;
/**
* RSA最大解密密文大小
*/
private static final int MAX_DECRYPT_BLOCK = KEY_SIZE / 8;
/**
* RSA最大加密明文大小
*/
private static final int MAX_ENCRYPT_BLOCK = MAX_DECRYPT_BLOCK - 11;
private static Map<Integer,String> keyMap=new HashMap<>();
/**
* 随机生成密钥对
* @throws NoSuchAlgorithmException
*/
public static void getKeyPair() throws Exception {
//KeyPairGenerator类用于生成公钥和密钥对基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
//初始化密钥对生成器密钥大小为96-1024位
keyPairGen.initialize(1024,new SecureRandom());
//生成一个密钥对保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();//得到私钥
PublicKey publicKey = keyPair.getPublic();//得到公钥
//得到公钥字符串
String publicKeyString=new String(Base64.encodeBase64(publicKey.getEncoded()));
//得到私钥字符串
String privateKeyString=new String(Base64.encodeBase64(privateKey.getEncoded()));
//将公钥和私钥保存到Map
keyMap.put(0,publicKeyString);//0表示公钥
keyMap.put(1,privateKeyString);//1表示私钥
}
/**
* <p>
* 公钥加密
* </p>
*
* @param str 源数据
* @param publicKey 公钥(BASE64编码)
* @return
* @throws Exception
*/
public static String encryptByPublicKey(String str, String publicKey) throws Exception {
publicKey=publicKey.replace("-----BEGIN PUBLIC KEY-----","").replace("-----END PUBLIC KEY-----","");
byte[] data=Base64.decodeBase64(str);
byte[] keyBytes = Base64.decodeBase64(publicKey);
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
Key publicK = keyFactory.generatePublic(x509KeySpec);
// 对数据加密
Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicK);
int inputLen = data.length;
ByteArrayOutputStream out = new ByteArrayOutputStream();
int offSet = 0;
byte[] cache;
int i = 0;
// 对数据分段加密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
} else {
cache = cipher.doFinal(data, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_ENCRYPT_BLOCK;
}
byte[] encryptedData = out.toByteArray();
out.close();
return new String(Base64.encodeBase64(encryptedData));
}
/**
* RSA私钥解密
*
* @param str
* 加密字符串
* @param privateKey
* 私钥
* @return 铭文
* @throws Exception
* 解密过程中的异常信息
*/
public static String decrypt(String str,String privateKey) throws Exception {
//Base64解码加密后的字符串
byte[] inputByte = Base64.decodeBase64(str.getBytes("UTF-8"));
//Base64编码的私钥
byte[] decoded = Base64.decodeBase64(privateKey);
PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE,priKey);
String outStr=new String(cipher.doFinal(inputByte));
return outStr;
}
}