parent
f3aa0d4be0
commit
036c4b47be
@ -0,0 +1,95 @@ |
|||||||
|
package cn.linghang.mywust.core.service.auth; |
||||||
|
|
||||||
|
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
||||||
|
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
||||||
|
import cn.linghang.mywust.core.exception.ApiException; |
||||||
|
import cn.linghang.mywust.core.exception.BasicException; |
||||||
|
import cn.linghang.mywust.core.request.graduate.GraduateRequestFactory; |
||||||
|
import cn.linghang.mywust.core.service.captcha.solver.CaptchaSolver; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.network.entitys.HttpRequest; |
||||||
|
import cn.linghang.mywust.network.entitys.HttpResponse; |
||||||
|
|
||||||
|
import javax.imageio.ImageIO; |
||||||
|
import java.awt.*; |
||||||
|
import java.awt.image.BufferedImage; |
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.ByteArrayOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class GraduateLogin { |
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
private final CaptchaSolver captchaSolver; |
||||||
|
|
||||||
|
public GraduateLogin(Requester requester, CaptchaSolver captchaSolver) { |
||||||
|
this.requester = requester; |
||||||
|
this.captchaSolver = captchaSolver; |
||||||
|
} |
||||||
|
|
||||||
|
public String getLoginCookie(String username, String password, RequestClientOption option) throws IOException, BasicException { |
||||||
|
// 请求获取验证码
|
||||||
|
HttpRequest captchaImageRequest = GraduateRequestFactory.captchaRequest(); |
||||||
|
HttpResponse captchaImageResponse = requester.get(captchaImageRequest, option); |
||||||
|
|
||||||
|
UnsolvedImageCaptcha unsolvedImageCaptcha = new UnsolvedImageCaptcha(); |
||||||
|
unsolvedImageCaptcha.setBindInfo(captchaImageResponse.getCookies()); |
||||||
|
|
||||||
|
byte[] processedImage = ImageUtil.process(captchaImageResponse.getBody()); |
||||||
|
unsolvedImageCaptcha.setImage(processedImage); |
||||||
|
|
||||||
|
// 通过传入的captchaSolver来处理验证码
|
||||||
|
SolvedImageCaptcha solvedImageCaptcha = captchaSolver.solve(unsolvedImageCaptcha); |
||||||
|
|
||||||
|
// 进行登录
|
||||||
|
HttpRequest loginRequest = GraduateRequestFactory.loginRequest(username, password, solvedImageCaptcha); |
||||||
|
HttpResponse loginResponse = requester.post(loginRequest, option); |
||||||
|
|
||||||
|
// 登陆成功,应该会是302跳转,不是的话多半是认证错误
|
||||||
|
if (loginResponse.getStatusCode() != HttpResponse.HTTP_REDIRECT_302) { |
||||||
|
throw new ApiException(ApiException.Code.GRADUATE_PASSWORD_WRONG); |
||||||
|
} |
||||||
|
|
||||||
|
// 使用当初通过验证码得到的cookie来作为登录cookie,至于是否真正可行待验证
|
||||||
|
return captchaImageResponse.getCookies(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class ImageUtil { |
||||||
|
// 验证码图片的长宽
|
||||||
|
private static final int IMAGE_WIDTH = 75; |
||||||
|
private static final int IMAGE_HEIGHT = 35; |
||||||
|
|
||||||
|
// 二值化阈值,实测339效果最佳
|
||||||
|
private static final int LIMIT = 339; |
||||||
|
|
||||||
|
private static final int BLACK = Color.BLACK.getRGB(); |
||||||
|
private static final int WHITE = Color.WHITE.getRGB(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 初步处理验证码图片,对图片去色处理以更好地进行ocr处理 |
||||||
|
*/ |
||||||
|
public static byte[] process(byte[] data) throws ApiException { |
||||||
|
try { |
||||||
|
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data); |
||||||
|
BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream); |
||||||
|
|
||||||
|
for (int i = 0; i < IMAGE_WIDTH; ++i) { |
||||||
|
for (int j = 0; j < IMAGE_HEIGHT; ++j) { |
||||||
|
int rgb = bufferedImage.getRGB(i, j); |
||||||
|
int bright = ((rgb >> 16) & 0xff) + ((rgb >> 8) & 0xff) + (rgb & 0xff); |
||||||
|
|
||||||
|
bufferedImage.setRGB(i, j, bright <= LIMIT ? BLACK : WHITE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
||||||
|
ImageIO.write(bufferedImage, "png", outputStream); |
||||||
|
|
||||||
|
return byteArrayInputStream.readAllBytes(); |
||||||
|
} catch (Exception e) { |
||||||
|
throw new ApiException(ApiException.Code.INTERNAL_EXCEPTION); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,8 +1,9 @@ |
|||||||
package cn.linghang.mywust.captcha.solver; |
package cn.linghang.mywust.core.service.captcha.solver; |
||||||
|
|
||||||
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
||||||
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
||||||
|
import cn.linghang.mywust.core.exception.ApiException; |
||||||
|
|
||||||
public interface CaptchaSolver { |
public interface CaptchaSolver { |
||||||
SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha); |
SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) throws ApiException; |
||||||
} |
} |
@ -0,0 +1,77 @@ |
|||||||
|
package cn.linghang.mywust.core.service.captcha.solver; |
||||||
|
|
||||||
|
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
||||||
|
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
||||||
|
import cn.linghang.mywust.core.exception.ApiException; |
||||||
|
import cn.linghang.mywust.core.request.RequestFactory; |
||||||
|
import cn.linghang.mywust.network.RequestClientOption; |
||||||
|
import cn.linghang.mywust.network.Requester; |
||||||
|
import cn.linghang.mywust.network.entitys.HttpRequest; |
||||||
|
import cn.linghang.mywust.network.entitys.HttpResponse; |
||||||
|
import cn.linghang.mywust.util.StringUtil; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import org.apache.commons.codec.binary.Base64; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.TreeMap; |
||||||
|
|
||||||
|
public class LinghangOcrServiceCaptchaSolver implements CaptchaSolver { |
||||||
|
private final String baseUrl; |
||||||
|
private final Requester requester; |
||||||
|
|
||||||
|
private final String appId; |
||||||
|
private final String secretKey; |
||||||
|
|
||||||
|
private static final RequestClientOption requestOption = new RequestClientOption(); |
||||||
|
|
||||||
|
public LinghangOcrServiceCaptchaSolver(String baseUrl, Requester requester, |
||||||
|
String appId, String secretKey) { |
||||||
|
|
||||||
|
this.baseUrl = baseUrl; |
||||||
|
this.requester = requester; |
||||||
|
|
||||||
|
this.appId = appId; |
||||||
|
this.secretKey = secretKey; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) throws ApiException { |
||||||
|
Map<String, String> urlParams = new TreeMap<>(String::compareTo); |
||||||
|
urlParams.put("id", appId); |
||||||
|
urlParams.put("t", Long.toString(System.currentTimeMillis() / 1000)); |
||||||
|
urlParams.put("type", "json"); |
||||||
|
|
||||||
|
String sign = StringUtil.generateSignText(appId, secretKey, urlParams); |
||||||
|
urlParams.put("sign", sign); |
||||||
|
|
||||||
|
String url = baseUrl + StringUtil.generateQueryString(urlParams); |
||||||
|
byte[] imageBase64Data = Base64.encodeBase64(unsolvedImageCaptcha.getImage()); |
||||||
|
|
||||||
|
HttpRequest request; |
||||||
|
HttpResponse response; |
||||||
|
OcrResult result; |
||||||
|
try { |
||||||
|
request = RequestFactory.makeHttpRequest(url, imageBase64Data); |
||||||
|
response = requester.post(request, requestOption); |
||||||
|
result = new ObjectMapper().readValue(response.getBody(), OcrResult.class); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new ApiException(ApiException.Code.NETWORK_EXCEPTION); |
||||||
|
} |
||||||
|
|
||||||
|
if (result.code != OcrResult.CODE_SUCCESS) { |
||||||
|
throw new ApiException(ApiException.Code.INTERNAL_EXCEPTION); |
||||||
|
} |
||||||
|
|
||||||
|
return new SolvedImageCaptcha(unsolvedImageCaptcha, result.result); |
||||||
|
} |
||||||
|
|
||||||
|
private static class OcrResult { |
||||||
|
private int code; |
||||||
|
private String result; |
||||||
|
private String message; |
||||||
|
|
||||||
|
public static final int CODE_SUCCESS = 0; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,4 +1,4 @@ |
|||||||
package cn.linghang.mywust.captcha.solver; |
package cn.linghang.mywust.core.service.captcha.solver; |
||||||
|
|
||||||
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
||||||
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
@ -1,25 +1,37 @@ |
|||||||
package cn.linghang.mywust.captcha; |
package cn.linghang.mywust.captcha; |
||||||
|
|
||||||
import lombok.Data; |
import lombok.Data; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
/** |
/** |
||||||
* 已处理的图片验证码 |
* 已处理的图片验证码,必须要和特定的未处理验证码绑定 |
||||||
*/ |
*/ |
||||||
@Data |
@Getter |
||||||
public class SolvedImageCaptcha { |
public class SolvedImageCaptcha { |
||||||
/** |
/** |
||||||
* 和验证码绑定的信息,如cookie,某种id等 |
* 和验证码绑定的信息,如cookie,某种id等 |
||||||
*/ |
*/ |
||||||
private String bindInfo; |
private final String bindInfo; |
||||||
|
|
||||||
/** |
/** |
||||||
* 验证码验证数据 |
* 验证码验证数据 |
||||||
*/ |
*/ |
||||||
private String result; |
private String result; |
||||||
|
|
||||||
|
public SolvedImageCaptcha(String bindInfo) { |
||||||
|
this.bindInfo = bindInfo; |
||||||
|
} |
||||||
|
|
||||||
public SolvedImageCaptcha(UnsolvedImageCaptcha unsolvedImageCaptcha) { |
public SolvedImageCaptcha(UnsolvedImageCaptcha unsolvedImageCaptcha) { |
||||||
this.bindInfo = unsolvedImageCaptcha.getBindInfo(); |
this(unsolvedImageCaptcha.getBindInfo()); |
||||||
} |
} |
||||||
|
|
||||||
private void setBindInfo(String bindInfo) {} |
public SolvedImageCaptcha(UnsolvedImageCaptcha unsolvedImageCaptcha, String result) { |
||||||
|
this(unsolvedImageCaptcha); |
||||||
|
this.result = result; |
||||||
|
} |
||||||
|
|
||||||
|
public void setResult(String result) { |
||||||
|
this.result = result; |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -1,11 +0,0 @@ |
|||||||
package cn.linghang.mywust.captcha.solver; |
|
||||||
|
|
||||||
import cn.linghang.mywust.captcha.SolvedImageCaptcha; |
|
||||||
import cn.linghang.mywust.captcha.UnsolvedImageCaptcha; |
|
||||||
|
|
||||||
public class LinghangOcrServiceCaptchaSolver implements CaptchaSolver { |
|
||||||
@Override |
|
||||||
public SolvedImageCaptcha solve(UnsolvedImageCaptcha unsolvedImageCaptcha) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue