使用Okhttp的Requester,简单的实现了mywust的需求,能应付普通的使用
+ *可以设置OkhttpClient是否使用单例模式以节省部分内存和对象分配的消耗,默认不使用(多例模式)
+ *按照Okhttp官方的说法以及其他人的测试,使用多例Okhttp对性能消耗其实并不是很大,可以忽略,在多例模式下性能可能也比httpclient要好
+ *但是如果每次请求的代理或者超时等设置有动态需求的话只能使用多例模式,单例模式下OkhttpClient的设置是全局一致的
+ *如果有更加高级的需求实现,也可以自己实现Requester接口
+ * + * @author lensfrex + * @create 2022-10-15 09:49 + * @edit 2022-10-15 09:49 + */ +public class SimpleOkhttpRequester implements Requester { + private final boolean singletonClient; + + public SimpleOkhttpRequester() { + singletonClient = false; + } + + public SimpleOkhttpRequester(boolean singletonClient) { + this.singletonClient = singletonClient; + } + + private static volatile OkHttpClient singletonOkhttp; + + private static OkHttpClient newOkhttpClient(RequestClientOption requestClientOption) { + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .callTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS) + .readTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS) + .connectTimeout(requestClientOption.getTimeout(), TimeUnit.SECONDS); + + // 设置代理 + RequestClientOption.Proxy proxyOption = requestClientOption.getProxy(); + if (proxyOption != null) { + InetSocketAddress proxyAddress = new InetSocketAddress(proxyOption.getAddress(), proxyOption.getPort()); + Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyAddress); + + builder.proxy(proxy); + } + + return builder.build(); + } + + private static OkHttpClient getSingletonClientInstant(RequestClientOption options) { + if (singletonOkhttp == null) { + synchronized (SimpleOkhttpRequester.class) { + if (singletonOkhttp == null) { + singletonOkhttp = newOkhttpClient(options); + } + } + } + + return singletonOkhttp; + } + + private OkHttpClient getOkhttpClient(RequestClientOption options) { + if (singletonClient) { + return getSingletonClientInstant(options); + } else { + return newOkhttpClient(options); + } + } + + private static final String JSON_CONTENT_TYPE = "application/json; charset=utf-8"; + + private static final String DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"; + + public enum RequestMethod { + GET, POST + } + + private Request buildRequest(RequestMethod requestMethod, URL url, HttpRequest httpRequest) { + Request.Builder requestBuilder = new Request.Builder().url(url); + + Map请求类的上层封装接口,传入参数直接进行请求
+ * + *实现了这个接口的类就可以提供给core使用
+ * + *例如,可以使用okhttp和httpclient实现底层网络请求(当然也可以自己实现类似的),在上层中只需要调用get或者post即可
+ * + * @author lenfrex + * @create 2022-10-14 20:13 + */ +public interface Requester { + + HttpResponse get(URL url, HttpRequest request, RequestClientOption requestClientOption) throws Exception; + + HttpResponse post(URL url, HttpRequest request, RequestClientOption options) throws Exception; + + HttpResponse postJson(URL url, HttpRequest request, Object requestBody, RequestClientOption options) throws Exception; +} diff --git a/mywust-util/pom.xml b/mywust-util/pom.xml index 64371f5..aed53ac 100644 --- a/mywust-util/pom.xml +++ b/mywust-util/pom.xml @@ -17,4 +17,11 @@使用统一登陆的加密方法对密码进行加密的工具类
+ *研究发现统一认证登录对密码的加密方式只是简单的RSA NoPadding加密(虽然但是,貌似私钥也放在前端了...)
+ *加密过程大概如下:
+ *- 通过modulus模数和exponent指数生成rsa公钥
+ *- 将明文反转后用上面生成的公钥进行RSA加密,明文填充使用NoPadding填充(0填充)
+ *- 将生成的字节数组转换成16进制字符串,得到加密后的数据
+ *希望不要再改登录方式了 [拜]
+ * @author : lensfrex + * @description : 使用统一登陆的加密方法对密码进行加密的工具类 + * @create : 2022-09-18 14:42:06 + * @edit : 2022-10-14 14:42:06 + */ +public class PasswordEncoder { + private static final String MODULUS = "b5eeb166e069920e80bebd1fea4829d3d1f3216f2aabe79b6c47a3c18dcee5fd22c2e7ac519cab59198ece036dcf289ea8201e2a0b9ded307f8fb704136eaeb670286f5ad44e691005ba9ea5af04ada5367cd724b5a26fdb5120cc95b6431604bd219c6b7d83a6f8f24b43918ea988a76f93c333aa5a20991493d4eb1117e7b1"; + private static final String EXPONENT = "10001"; + + private static final RSAPublicKey PUBLIC_KEY = PasswordEncoder.generatePublicKey(); + + private static Cipher cipher; + + // 单例加密/解密器 + static { + try { + cipher = Cipher.getInstance("RSA/ECB/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, PUBLIC_KEY); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { + // 会报错的地方都是因为算法不正确,这里都是固定的值,出错了就是程序写错了,在测试的时候就应该发现的问题 + // 对于调用者不必处理这种异常,但是开发测试的时候应该要打日志来发现问题 + System.err.println("加密出错"); + e.printStackTrace(); + } + } + + /** + * 按照统一登陆的加密方法,加密密码 + * + * @param password 密码明文 + * @return 加密后的密码(小写) + */ + public static String encodePassword(String password) { + try { + // 反转明文 + byte[] reversedTextData = new StringBuffer(password) + .reverse() + .toString() + .getBytes(StandardCharsets.UTF_8); + + // 完成加密 + return Hex.encodeHexString(cipher.doFinal(reversedTextData)); + } catch (IllegalBlockSizeException | BadPaddingException e) { + // 会报错的地方都是因为算法不正确,这里都是固定的值,出错了就是程序写错了,在测试的时候就应该发现的问题 + // 对于调用者不必处理这种异常,但是开发测试的时候应该要打日志来发现问题 + System.err.println("加密出错"); + e.printStackTrace(); + } + + return ""; + } + + /** + * 使用modulus模数和exponent指数生成rsa公钥 + * + * @return 公钥 + */ + private static RSAPublicKey generatePublicKey() { + try { + BigInteger mod = new BigInteger(PasswordEncoder.MODULUS, 16); + BigInteger exp = new BigInteger(PasswordEncoder.EXPONENT, 16); + + RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, exp); + + return (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + // 会报错的地方都是因为算法不正确,这里都是固定的值,出错了就是程序写错了,在测试的时候就应该发现的问题 + // 对于调用者不必处理这种异常,但是开发测试的时候应该要打日志来发现问题 + System.err.println("生成公钥出错"); + e.printStackTrace(); + } + + return null; + } +}