From 0869a5ca7f339e936ed54365b7d361b7e36a2b3a Mon Sep 17 00:00:00 2001 From: Sola Date: Sun, 13 Dec 2015 01:54:11 +0800 Subject: [PATCH] use RSA to encrypt login --- pom.xml | 4 + .../love/sola/netsupport/api/Authorize.java | 2 +- .../love/sola/netsupport/api/TicketQuery.java | 2 +- .../love/sola/netsupport/api/admin/Login.java | 4 +- .../netsupport/api/admin/TicketQuery.java | 10 +++ .../netsupport/api/admin/TicketUpdate.java | 82 +++++++++++++++++++ .../love/sola/netsupport/pojo/Ticket.java | 3 + .../love/sola/netsupport/util/AESUtil.java | 51 ------------ .../love/sola/netsupport/util/Checker.java | 10 ++- .../love/sola/netsupport/util/Crypto.java | 2 +- .../love/sola/netsupport/util/ParseUtil.java | 8 +- .../love/sola/netsupport/util/RSAUtil.java | 61 ++++++++++++++ .../wechat/handler/LoginHandler.java | 3 +- src/main/resources/lang.yml | 4 +- .../sola/netsupport/wechat/TestEncrypt.java | 8 +- 15 files changed, 183 insertions(+), 71 deletions(-) create mode 100644 src/main/java/love/sola/netsupport/api/admin/TicketQuery.java create mode 100644 src/main/java/love/sola/netsupport/api/admin/TicketUpdate.java delete mode 100644 src/main/java/love/sola/netsupport/util/AESUtil.java create mode 100644 src/main/java/love/sola/netsupport/util/RSAUtil.java diff --git a/pom.xml b/pom.xml index 72cca5b..5c395b4 100644 --- a/pom.xml +++ b/pom.xml @@ -144,6 +144,10 @@ 1.0.0.Final + org.hibernate + hibernate-envers + 5.0.3.Final + de.svenkubiak jBCrypt 0.4 diff --git a/src/main/java/love/sola/netsupport/api/Authorize.java b/src/main/java/love/sola/netsupport/api/Authorize.java index e5ea65c..4fca2b4 100644 --- a/src/main/java/love/sola/netsupport/api/Authorize.java +++ b/src/main/java/love/sola/netsupport/api/Authorize.java @@ -62,7 +62,7 @@ public class Authorize extends HttpServlet { l = System.currentTimeMillis(); } - if (!Checker.nonNull(c, l)) { + if (Checker.hasNull(c, l)) { return new Response(Response.ResponseCode.AUTHORIZE_FAILED); } if (l < System.currentTimeMillis() - Settings.I.User_Command_Timeout * 1000) { diff --git a/src/main/java/love/sola/netsupport/api/TicketQuery.java b/src/main/java/love/sola/netsupport/api/TicketQuery.java index 05cf35e..9cbc024 100644 --- a/src/main/java/love/sola/netsupport/api/TicketQuery.java +++ b/src/main/java/love/sola/netsupport/api/TicketQuery.java @@ -28,7 +28,7 @@ import java.io.PrintWriter; * Don't modify this source without my agreement * *********************************************** */ -@WebServlet(name = "QueryTicket", urlPatterns = "/api/ticketquery", loadOnStartup = 24) +@WebServlet(name = "TicketQuery", urlPatterns = "/api/ticketquery", loadOnStartup = 24) public class TicketQuery extends HttpServlet { private Gson gson = SQLCore.gson; diff --git a/src/main/java/love/sola/netsupport/api/admin/Login.java b/src/main/java/love/sola/netsupport/api/admin/Login.java index 38234a6..62ec7b6 100644 --- a/src/main/java/love/sola/netsupport/api/admin/Login.java +++ b/src/main/java/love/sola/netsupport/api/admin/Login.java @@ -49,7 +49,7 @@ public class Login extends HttpServlet { String wechat = request.getParameter("wechat"); String opId = request.getParameter("op"); String password = request.getParameter("pass"); - if (Checker.nonNull(wechat, opId, password)) return new Response(Response.ResponseCode.PARAMETER_REQUIRED); + if (Checker.hasNull(wechat, opId, password)) return new Response(Response.ResponseCode.PARAMETER_REQUIRED); try (Session s = SQLCore.sf.openSession()) { Operator operator = s.get(Operator.class, Integer.parseInt(opId)); @@ -63,7 +63,7 @@ public class Login extends HttpServlet { HttpSession httpSession = request.getSession(true); httpSession.setAttribute("wechat", wechat); httpSession.setAttribute("operator", operator); - return new Response(Response.ResponseCode.OK); + return new Response(Response.ResponseCode.OK, operator); } catch (NumberFormatException e) { return new Response(Response.ResponseCode.ILLEGAL_PARAMETER); } catch (HibernateException e) { diff --git a/src/main/java/love/sola/netsupport/api/admin/TicketQuery.java b/src/main/java/love/sola/netsupport/api/admin/TicketQuery.java new file mode 100644 index 0000000..8819791 --- /dev/null +++ b/src/main/java/love/sola/netsupport/api/admin/TicketQuery.java @@ -0,0 +1,10 @@ +package love.sola.netsupport.api.admin; + +/** + * *********************************************** + * Created by Sola on 2015/12/13. + * Don't modify this source without my agreement + * *********************************************** + */ +public class TicketQuery { +} diff --git a/src/main/java/love/sola/netsupport/api/admin/TicketUpdate.java b/src/main/java/love/sola/netsupport/api/admin/TicketUpdate.java new file mode 100644 index 0000000..7bcb6ac --- /dev/null +++ b/src/main/java/love/sola/netsupport/api/admin/TicketUpdate.java @@ -0,0 +1,82 @@ +package love.sola.netsupport.api.admin; + +import com.google.gson.Gson; +import love.sola.netsupport.api.Response; +import love.sola.netsupport.pojo.Operator; +import love.sola.netsupport.pojo.Ticket; +import love.sola.netsupport.sql.SQLCore; +import love.sola.netsupport.util.Checker; +import love.sola.netsupport.util.ParseUtil; +import org.hibernate.HibernateException; +import org.hibernate.Session; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * *********************************************** + * Created by Sola on 2015/12/13. + * Don't modify this source without my agreement + * *********************************************** + */ +@WebServlet(name = "TicketUpdate", urlPatterns = "/api/ticketupdate", loadOnStartup = 32) +public class TicketUpdate extends HttpServlet { + + private Gson gson = SQLCore.gson; + + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + doGet(request, response); + } + + @SuppressWarnings("Duplicates") + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + request.setCharacterEncoding("utf-8"); + response.setCharacterEncoding("utf-8"); + response.addHeader("Content-type", "text/json;charset=utf-8"); + PrintWriter out = response.getWriter(); + String json = gson.toJson(update(request)); + out.println(ParseUtil.parseJsonP(request, json)); + out.close(); + } + + private Response update(HttpServletRequest request) { + String ticket = request.getParameter("ticket"); + String remark = request.getParameter("remark"); + String status = request.getParameter("status"); + if (Checker.hasNull(ticket, remark, status)) return new Response(Response.ResponseCode.PARAMETER_REQUIRED); + + try (Session s = SQLCore.sf.openSession()) { + HttpSession httpSession = request.getSession(false); + if (!Checker.operator(httpSession)) { + return new Response(Response.ResponseCode.UNAUTHORIZED); + } + Operator op = (Operator) httpSession.getAttribute("operator"); + Ticket t = s.get(Ticket.class, Integer.parseInt(ticket)); + if (t == null) { + return new Response(Response.ResponseCode.TICKET_NOT_FOUND); + } + t.setOperator(op); + t.setRemark(remark); + t.setStatus(Integer.parseInt(status)); + s.beginTransaction(); + s.update(t); + s.getTransaction().commit(); + return new Response(Response.ResponseCode.OK, t); + } catch (NumberFormatException e) { + return new Response(Response.ResponseCode.ILLEGAL_PARAMETER); + } catch (HibernateException e) { + e.printStackTrace(); + return new Response(Response.ResponseCode.DATABASE_ERROR, e); + } catch (Exception e) { + e.printStackTrace(); + return new Response(Response.ResponseCode.INTERNAL_ERROR, e); + } + } + +} diff --git a/src/main/java/love/sola/netsupport/pojo/Ticket.java b/src/main/java/love/sola/netsupport/pojo/Ticket.java index be840a9..931255f 100644 --- a/src/main/java/love/sola/netsupport/pojo/Ticket.java +++ b/src/main/java/love/sola/netsupport/pojo/Ticket.java @@ -4,6 +4,8 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import love.sola.netsupport.sql.TableTicket; +import org.hibernate.envers.Audited; +import org.hibernate.envers.RelationTargetAuditMode; import javax.persistence.*; import java.util.Date; @@ -19,6 +21,7 @@ import java.util.Date; @NoArgsConstructor @Entity @Table(name = "tickets") +@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) public class Ticket { public static final String PROPERTY_USER = "user"; diff --git a/src/main/java/love/sola/netsupport/util/AESUtil.java b/src/main/java/love/sola/netsupport/util/AESUtil.java deleted file mode 100644 index 856e217..0000000 --- a/src/main/java/love/sola/netsupport/util/AESUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -package love.sola.netsupport.util; - -import org.apache.commons.codec.binary.Base64; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; - -public class AESUtil { - - public static final byte[] initVector = "RandomInitVector".getBytes(StandardCharsets.UTF_8); - public static final byte[] key = "$bitch@sola.love".getBytes(StandardCharsets.UTF_8); - - public static String encrypt(String value) { - try { - IvParameterSpec iv = new IvParameterSpec(initVector); - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); - - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); - cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv); - - byte[] encrypted = cipher.doFinal(value.getBytes()); - - return Base64.encodeBase64String(encrypted); - } catch (Exception ex) { - ex.printStackTrace(); - } - - return null; - } - - public static String decrypt(String encrypted) { - try { - IvParameterSpec iv = new IvParameterSpec(initVector); - SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); - - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); - cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv); - - byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); - - return new String(original); - } catch (Exception ex) { - ex.printStackTrace(); - } - - return null; - } - -} \ No newline at end of file diff --git a/src/main/java/love/sola/netsupport/util/Checker.java b/src/main/java/love/sola/netsupport/util/Checker.java index 1f6b4ad..c947cb8 100644 --- a/src/main/java/love/sola/netsupport/util/Checker.java +++ b/src/main/java/love/sola/netsupport/util/Checker.java @@ -12,13 +12,17 @@ import javax.servlet.http.HttpSession; */ public class Checker { - public static boolean nonNull(Object... v) { - for (Object o : v) if (o == null) return false; - return true; + public static boolean hasNull(Object... v) { + for (Object o : v) if (o == null) return true; + return false; } public static boolean authorized(HttpSession s, Command c) { return s != null && s.getAttribute("authorized") == c; } + public static boolean operator(HttpSession s) { + return s != null && s.getAttribute("operator") != null; + } + } diff --git a/src/main/java/love/sola/netsupport/util/Crypto.java b/src/main/java/love/sola/netsupport/util/Crypto.java index 024e8c4..6a5fc13 100644 --- a/src/main/java/love/sola/netsupport/util/Crypto.java +++ b/src/main/java/love/sola/netsupport/util/Crypto.java @@ -15,7 +15,7 @@ public class Crypto { } public static boolean check(String plain, String hash) { - return BCrypt.checkpw(AESUtil.decrypt(plain), hash); + return BCrypt.checkpw(RSAUtil.decrypt(plain), hash); } } diff --git a/src/main/java/love/sola/netsupport/util/ParseUtil.java b/src/main/java/love/sola/netsupport/util/ParseUtil.java index 1508ff7..55aba13 100644 --- a/src/main/java/love/sola/netsupport/util/ParseUtil.java +++ b/src/main/java/love/sola/netsupport/util/ParseUtil.java @@ -23,11 +23,9 @@ public class ParseUtil { .append(lang("Ticket_Info_Id")).append(t.getId()).append("\n") .append(lang("Ticket_Info_Desc")).append(t.getDescription()).append("\n") .append(lang("Ticket_Info_Submit_Time")).append(dateFormat.format(t.getSubmitTime())).append("\n"); - if (t.getUpdateTime() != null) { - sb.append(lang("Ticket_Info_Operator")).append(t.getOperator().getId()).append("\n"); - sb.append(lang("Ticket_Info_Remark")).append(t.getRemark()).append("\n"); - sb.append(lang("Ticket_Info_Update_Time")).append(dateFormat.format(t.getUpdateTime())).append("\n"); - } + if (t.getOperator() != null) sb.append(lang("Ticket_Info_Operator")).append(t.getOperator().getId()).append("\n"); + if (t.getRemark() != null) sb.append(lang("Ticket_Info_Remark")).append(t.getRemark()).append("\n"); + if (t.getUpdateTime() != null) sb.append(lang("Ticket_Info_Update_Time")).append(dateFormat.format(t.getUpdateTime())).append("\n"); sb.append(lang("Ticket_Info_Status")).append(Status.getLocalized(t.getStatus())); return sb.toString(); } diff --git a/src/main/java/love/sola/netsupport/util/RSAUtil.java b/src/main/java/love/sola/netsupport/util/RSAUtil.java new file mode 100644 index 0000000..25ce40b --- /dev/null +++ b/src/main/java/love/sola/netsupport/util/RSAUtil.java @@ -0,0 +1,61 @@ +package love.sola.netsupport.util; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; + +public class RSAUtil { + + + public static Key publicKey; + public static Key privateKey; + public static String publicKey_s; + public static String privateKey_s; + + static { + genKeyPair(); + } + + public static void genKeyPair() { + try { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(1024); + KeyPair kp = kpg.genKeyPair(); + publicKey = kp.getPublic(); + privateKey = kp.getPrivate(); + publicKey_s = Base64.encodeBase64String(publicKey.getEncoded()); + privateKey_s = Base64.encodeBase64String(privateKey.getEncoded()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static String encrypt(String value) { + try { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeBase64String(encrypted); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } + + public static String decrypt(String encrypted) { + try { + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted)); + return new String(original, StandardCharsets.UTF_8); + } catch (Exception ex) { + ex.printStackTrace(); + } + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/love/sola/netsupport/wechat/handler/LoginHandler.java b/src/main/java/love/sola/netsupport/wechat/handler/LoginHandler.java index cd48f4d..c1f0280 100644 --- a/src/main/java/love/sola/netsupport/wechat/handler/LoginHandler.java +++ b/src/main/java/love/sola/netsupport/wechat/handler/LoginHandler.java @@ -1,6 +1,7 @@ package love.sola.netsupport.wechat.handler; import love.sola.netsupport.sql.TableOperator; +import love.sola.netsupport.util.RSAUtil; import me.chanjar.weixin.common.exception.WxErrorException; import me.chanjar.weixin.common.session.WxSessionManager; import me.chanjar.weixin.mp.api.WxMpMessageHandler; @@ -28,7 +29,7 @@ public class LoginHandler implements WxMpMessageHandler { if (!TableOperator.has(wxMessage.getFromUserName())) { return out.content(lang("Not_Operator")).build(); } else { - return out.content(format("Operator_Login_Link", wxMessage.getFromUserName())).build(); + return out.content(format("Operator_Login_Link", wxMessage.getFromUserName(), RSAUtil.publicKey_s)).build(); } } diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml index 5a27ec9..a2165ca 100644 --- a/src/main/resources/lang.yml +++ b/src/main/resources/lang.yml @@ -40,10 +40,10 @@ Not_Operator: '嘟嘟嘟……' #URL User_Register_Link: "您尚未进行微信绑定, 请点击这里进行微信绑定操作。" -User_Query_Link: 'http://topaz.sinaapp.com/nm/v1/query.php?wechatid={0}' +User_Query_Link: 'http://topaz.sinaapp.com/nm/v1/query.html?wechatid={0}' User_Submit_Link: 'http://topaz.sinaapp.com/nm/v1/rrepair.html?wechatid={0}&name={1}&isp={2}&room={3}&block={4}&phone={5}' Result_Page: 'http://topaz.sinaapp.com/nm/v1/result.html' -Operator_Login_Link: 'CLICK HERE' +Operator_Login_Link: 'CLICK HERE' #Localized #Status diff --git a/src/test/java/love/sola/netsupport/wechat/TestEncrypt.java b/src/test/java/love/sola/netsupport/wechat/TestEncrypt.java index e2682b0..5350044 100644 --- a/src/test/java/love/sola/netsupport/wechat/TestEncrypt.java +++ b/src/test/java/love/sola/netsupport/wechat/TestEncrypt.java @@ -1,6 +1,6 @@ package love.sola.netsupport.wechat; -import love.sola.netsupport.util.AESUtil; +import love.sola.netsupport.util.RSAUtil; import org.junit.Test; import org.mindrot.jbcrypt.BCrypt; @@ -19,9 +19,9 @@ public class TestEncrypt { } @Test - public void testAES() { - assert "Hello World".equals(AESUtil.decrypt(AESUtil.encrypt("Hello World"))); - assert "Encrypt".equals(AESUtil.decrypt(AESUtil.encrypt("Encrypt"))); + public void testRSA() { + assert "Hello World".equals(RSAUtil.decrypt(RSAUtil.encrypt("Hello World"))); + assert "Encrypt".equals(RSAUtil.decrypt(RSAUtil.encrypt("Encrypt"))); } }