Browse Source

feat: 日志记录访问IP

zhangying 9 months ago
parent
commit
883d77a4d7

+ 2 - 0
doc/待更新脚本.txt

@@ -0,0 +1,2 @@
+-- 2024-6-25 日志表增加访问者IP
+ALTER TABLE `sys_log` ADD COLUMN `IPAddress` varchar(255) NULL COMMENT 'IP地址' AFTER `UserID`;

+ 3 - 2
src/main/java/com/hz/employmentsite/controller/AccountController.java

@@ -11,6 +11,7 @@ import com.hz.employmentsite.util.DateUtils;
 import com.hz.employmentsite.util.DesUtils;
 import com.hz.employmentsite.util.JsonMapper;
 import com.hz.employmentsite.util.TokenUtils;
+import com.hz.employmentsite.util.ip.IpUtils;
 import com.hz.employmentsite.vo.DesModel;
 import com.hz.employmentsite.vo.MenuData;
 import com.hz.employmentsite.vo.user.UserInfoModel;
@@ -72,7 +73,7 @@ public class AccountController {
         user.dataRangeList = userService.getUserDataRange(user.getUserId());
         user.permissionList = accountService.getUserPerms(user.getUserId());
 
-        logService.save("登录", "", "登录", user.userId);
+        logService.save("登录", "", "登录", user.userId, IpUtils.getIpAddr(request));
 
         return RespGenerstor.success(user);
 
@@ -95,7 +96,7 @@ public class AccountController {
         user.permissionList = accountService.getUserPerms(user.getUserId());
         user.isResetPassword = !userService.getDefaultPassword().equalsIgnoreCase(desData[1]);
 
-        logService.save("登录", "", "登录", user.userId);
+        logService.save("登录", "", "登录", user.userId, IpUtils.getIpAddr(request));
 
         return RespGenerstor.success(user);
 

+ 7 - 4
src/main/java/com/hz/employmentsite/controller/OAuthController.java

@@ -1,20 +1,23 @@
 package com.hz.employmentsite.controller;
 
+import com.fasterxml.jackson.databind.JsonNode;
 import com.hz.employmentsite.AppConfig;
-import com.hz.employmentsite.filter.exception.*;
+import com.hz.employmentsite.filter.exception.BaseResponse;
+import com.hz.employmentsite.filter.exception.RespGenerstor;
 import com.hz.employmentsite.model.SysUser;
 import com.hz.employmentsite.services.service.AccountService;
 import com.hz.employmentsite.services.service.UserService;
 import com.hz.employmentsite.services.service.system.LogService;
 import com.hz.employmentsite.util.*;
+import com.hz.employmentsite.util.ip.IpUtils;
 import com.hz.employmentsite.vo.user.UserModel;
-import com.fasterxml.jackson.databind.JsonNode;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.servlet.http.HttpServletRequest;
 import java.net.URLDecoder;
 import java.time.LocalDateTime;
 import java.util.HashMap;
@@ -97,7 +100,7 @@ public class OAuthController {
     }
 
     @GetMapping("/oauthLogin")
-    public BaseResponse<String> oauthLogin(String appType, String type, String token, String ticket, String tm) {
+    public BaseResponse<String> oauthLogin(HttpServletRequest request, String appType, String type, String token, String ticket, String tm) {
         log.info("oauthLogin:ticket   " + ticket + "  ,  tm  " + tm + "  ,  appType  " + appType+ "  ,  type  " + type);
 
         String userCode = "";
@@ -165,7 +168,7 @@ public class OAuthController {
         user.dataRangeList = userService.getUserDataRange(user.getUserId());
         user.permissionList = accountService.getUserPerms(user.getUserId());
 
-        logService.save("登录","","单点登录",user.userId);
+        logService.save("登录","","单点登录",user.userId, IpUtils.getIpAddr(request));
 
         return RespGenerstor.success(user);
 

+ 5 - 3
src/main/java/com/hz/employmentsite/controller/system/LogController.java

@@ -1,14 +1,16 @@
 package com.hz.employmentsite.controller.system;
 
+import com.github.pagehelper.PageInfo;
 import com.hz.employmentsite.filter.exception.BaseResponse;
 import com.hz.employmentsite.filter.exception.RespGenerstor;
 import com.hz.employmentsite.services.service.AccountService;
 import com.hz.employmentsite.services.service.system.LogService;
+import com.hz.employmentsite.util.ip.IpUtils;
 import com.hz.employmentsite.vo.system.LogModel;
-import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletRequest;
 import java.util.Date;
 
 @RestController
@@ -28,8 +30,8 @@ public class LogController {
     }
 
     @PostMapping("/save")
-    public BaseResponse<Integer> save(@RequestBody LogModel data) {
-        logService.save(data.getPageName(),data.getPageUrl(),data.getActionName(), accountService.getLoginUserID());
+    public BaseResponse<Integer> save(HttpServletRequest request, @RequestBody LogModel data) {
+        logService.save(data.getPageName(),data.getPageUrl(),data.getActionName(), accountService.getLoginUserID(), IpUtils.getIpAddr(request));
         return RespGenerstor.success(true);
     }
 }

+ 10 - 0
src/main/java/com/hz/employmentsite/model/SysLog.java

@@ -13,6 +13,8 @@ public class SysLog {
 
     private String userID;
 
+    private String IPAddress;
+
     private Date logTime;
 
     public String getLogID() {
@@ -55,6 +57,14 @@ public class SysLog {
         this.userID = userID == null ? null : userID.trim();
     }
 
+    public String getIPAddress() {
+        return IPAddress;
+    }
+
+    public void setIPAddress(String IPAddress) {
+        this.IPAddress = IPAddress == null ? null : IPAddress.trim();
+    }
+
     public Date getLogTime() {
         return logTime;
     }

+ 70 - 0
src/main/java/com/hz/employmentsite/model/SysLogExample.java

@@ -455,6 +455,76 @@ public class SysLogExample {
             return (Criteria) this;
         }
 
+        public Criteria andIPAddressIsNull() {
+            addCriterion("IPAddress is null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressIsNotNull() {
+            addCriterion("IPAddress is not null");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressEqualTo(String value) {
+            addCriterion("IPAddress =", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressNotEqualTo(String value) {
+            addCriterion("IPAddress <>", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressGreaterThan(String value) {
+            addCriterion("IPAddress >", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressGreaterThanOrEqualTo(String value) {
+            addCriterion("IPAddress >=", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressLessThan(String value) {
+            addCriterion("IPAddress <", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressLessThanOrEqualTo(String value) {
+            addCriterion("IPAddress <=", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressLike(String value) {
+            addCriterion("IPAddress like", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressNotLike(String value) {
+            addCriterion("IPAddress not like", value, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressIn(List<String> values) {
+            addCriterion("IPAddress in", values, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressNotIn(List<String> values) {
+            addCriterion("IPAddress not in", values, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressBetween(String value1, String value2) {
+            addCriterion("IPAddress between", value1, value2, "IPAddress");
+            return (Criteria) this;
+        }
+
+        public Criteria andIPAddressNotBetween(String value1, String value2) {
+            addCriterion("IPAddress not between", value1, value2, "IPAddress");
+            return (Criteria) this;
+        }
+
         public Criteria andLogTimeIsNull() {
             addCriterion("LogTime is null");
             return (Criteria) this;

+ 4 - 3
src/main/java/com/hz/employmentsite/services/impl/system/LogServiceImpl.java

@@ -1,12 +1,12 @@
 package com.hz.employmentsite.services.impl.system;
 
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import com.hz.employmentsite.mapper.SysLogMapper;
 import com.hz.employmentsite.mapper.cquery.SysLogCQuery;
 import com.hz.employmentsite.model.SysLog;
 import com.hz.employmentsite.services.service.system.LogService;
 import com.hz.employmentsite.vo.system.LogModel;
-import com.github.pagehelper.PageHelper;
-import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -33,7 +33,7 @@ public class LogServiceImpl implements LogService {
         return result;
     }
 
-    public Integer save(String pageName, String pageUrl, String actionName, String userID) {
+    public Integer save(String pageName, String pageUrl, String actionName, String userID, String ipAddress) {
         try{
             SysLog sysLog = new SysLog();
             sysLog.setLogID(UUID.randomUUID().toString());
@@ -42,6 +42,7 @@ public class LogServiceImpl implements LogService {
             sysLog.setActionName(actionName);
             sysLog.setUserID(userID);
             sysLog.setLogTime(new Date());
+            sysLog.setIPAddress(ipAddress);
 
             sysLogMapper.insert(sysLog);
         }

+ 2 - 2
src/main/java/com/hz/employmentsite/services/service/system/LogService.java

@@ -1,7 +1,7 @@
 package com.hz.employmentsite.services.service.system;
 
-import com.hz.employmentsite.vo.system.LogModel;
 import com.github.pagehelper.PageInfo;
+import com.hz.employmentsite.vo.system.LogModel;
 
 import java.util.Date;
 
@@ -9,6 +9,6 @@ public interface LogService {
 
     PageInfo<LogModel> getList(Integer page, Integer rows, String loginID, String userName, Date startDate, Date endDate);
 
-    Integer save(String pageName,String pageUrl,String actionName, String userID);
+    Integer save(String pageName,String pageUrl,String actionName, String userID, String ipAddress);
 
 }

+ 40 - 0
src/main/java/com/hz/employmentsite/util/ip/AddressUtils.java

@@ -0,0 +1,40 @@
+package com.hz.employmentsite.util.ip;
+
+import com.alibaba.fastjson.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 获取地址类
+ *
+ * @author ruoyi
+ */
+public class AddressUtils {
+    // IP地址查询
+    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
+    // 未知地址
+    public static final String UNKNOWN = "XX XX";
+    private static final Logger log = LoggerFactory.getLogger(AddressUtils.class);
+
+    public static String getRealAddressByIP(String ip) {
+        String address = UNKNOWN;
+        // 内网不查询
+        if (IpUtils.internalIp(ip)) {
+            return "内网IP";
+        }
+        try {
+            String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", "GBK");
+            if (rspStr == null || "".equals(rspStr.trim())) {
+                log.error("获取地理位置异常 {}", ip);
+                return UNKNOWN;
+            }
+            JSONObject obj = JSONObject.parseObject(rspStr);
+            String region = obj.getString("pro");
+            String city = obj.getString("city");
+            return String.format("%s %s", region, city);
+        } catch (Exception e) {
+            log.error("获取地理位置异常 {}", ip);
+        }
+        return address;
+    }
+}

+ 138 - 0
src/main/java/com/hz/employmentsite/util/ip/EscapeUtil.java

@@ -0,0 +1,138 @@
+package com.hz.employmentsite.util.ip;
+
+/**
+ * 转义和反转义工具类
+ *
+ * @author ruoyi
+ */
+public class EscapeUtil {
+    public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
+
+    private static final char[][] TEXT = new char[64][];
+
+    static {
+        for (int i = 0; i < 64; i++) {
+            TEXT[i] = new char[]{(char) i};
+        }
+
+        // special HTML characters
+        TEXT['\''] = "&#039;".toCharArray(); // 单引号
+        TEXT['"'] = "&#34;".toCharArray(); // 双引号
+        TEXT['&'] = "&#38;".toCharArray(); // &符
+        TEXT['<'] = "&#60;".toCharArray(); // 小于号
+        TEXT['>'] = "&#62;".toCharArray(); // 大于号
+    }
+
+    /**
+     * 转义文本中的HTML字符为安全的字符
+     *
+     * @param text 被转义的文本
+     * @return 转义后的文本
+     */
+    public static String escape(String text) {
+        return encode(text);
+    }
+
+    /**
+     * 还原被转义的HTML特殊字符
+     *
+     * @param content 包含转义符的HTML内容
+     * @return 转换后的字符串
+     */
+    public static String unescape(String content) {
+        return decode(content);
+    }
+
+    /**
+     * 清除所有HTML标签,但是不删除标签内的内容
+     *
+     * @param content 文本
+     * @return 清除标签后的文本
+     */
+    public static String clean(String content) {
+        return new HTMLFilter().filter(content);
+    }
+
+    /**
+     * Escape编码
+     *
+     * @param text 被编码的文本
+     * @return 编码后的字符
+     */
+    private static String encode(String text) {
+        if (text == null || "".equals(text.trim())) {
+            return "";
+        }
+
+        final StringBuilder tmp = new StringBuilder(text.length() * 6);
+        char c;
+        for (int i = 0; i < text.length(); i++) {
+            c = text.charAt(i);
+            if (c < 256) {
+                tmp.append("%");
+                if (c < 16) {
+                    tmp.append("0");
+                }
+                tmp.append(Integer.toString(c, 16));
+            } else {
+                tmp.append("%u");
+                if (c <= 0xfff) {
+                    // issue#I49JU8@Gitee
+                    tmp.append("0");
+                }
+                tmp.append(Integer.toString(c, 16));
+            }
+        }
+        return tmp.toString();
+    }
+
+    /**
+     * Escape解码
+     *
+     * @param content 被转义的内容
+     * @return 解码后的字符串
+     */
+    public static String decode(String content) {
+        if (content == null || "".equals(content.trim())) {
+            return content;
+        }
+
+        StringBuilder tmp = new StringBuilder(content.length());
+        int lastPos = 0, pos = 0;
+        char ch;
+        while (lastPos < content.length()) {
+            pos = content.indexOf("%", lastPos);
+            if (pos == lastPos) {
+                if (content.charAt(pos + 1) == 'u') {
+                    ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 6;
+                } else {
+                    ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
+                    tmp.append(ch);
+                    lastPos = pos + 3;
+                }
+            } else {
+                if (pos == -1) {
+                    tmp.append(content.substring(lastPos));
+                    lastPos = content.length();
+                } else {
+                    tmp.append(content.substring(lastPos, pos));
+                    lastPos = pos;
+                }
+            }
+        }
+        return tmp.toString();
+    }
+
+    public static void main(String[] args) {
+        String html = "<script>alert(1);</script>";
+        String escape = EscapeUtil.escape(html);
+        // String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
+        // String html = "<123";
+        // String html = "123>";
+        System.out.println("clean: " + EscapeUtil.clean(html));
+        System.out.println("escape: " + escape);
+        System.out.println("unescape: " + EscapeUtil.unescape(escape));
+    }
+}

+ 497 - 0
src/main/java/com/hz/employmentsite/util/ip/HTMLFilter.java

@@ -0,0 +1,497 @@
+package com.hz.employmentsite.util.ip;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * HTML过滤器,用于去除XSS漏洞隐患。
+ *
+ * @author ruoyi
+ */
+public final class HTMLFilter {
+    /**
+     * regex flag union representing /si modifiers in php
+     **/
+    private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+    private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
+    private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+    private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+    private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+    private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+    private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+    private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+    private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+    private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
+    private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
+    private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+    private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+    private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+    private static final Pattern P_END_ARROW = Pattern.compile("^>");
+    private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+    private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+    private static final Pattern P_AMP = Pattern.compile("&");
+    private static final Pattern P_QUOTE = Pattern.compile("\"");
+    private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+    private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+    private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+    // @xxx could grow large... maybe use sesat's ReferenceMap
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
+    private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
+
+    /**
+     * set of allowed html elements, along with allowed attributes for each element
+     **/
+    private final Map<String, List<String>> vAllowed;
+    /**
+     * counts of open tags for each (allowable) html element
+     **/
+    private final Map<String, Integer> vTagCounts = new HashMap<>();
+
+    /**
+     * html elements which must always be self-closing (e.g. "<img />")
+     **/
+    private final String[] vSelfClosingTags;
+    /**
+     * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
+     **/
+    private final String[] vNeedClosingTags;
+    /**
+     * set of disallowed html elements
+     **/
+    private final String[] vDisallowed;
+    /**
+     * attributes which should be checked for valid protocols
+     **/
+    private final String[] vProtocolAtts;
+    /**
+     * allowed protocols
+     **/
+    private final String[] vAllowedProtocols;
+    /**
+     * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
+     **/
+    private final String[] vRemoveBlanks;
+    /**
+     * entities allowed within html markup
+     **/
+    private final String[] vAllowedEntities;
+    /**
+     * flag determining whether comments are allowed in input String.
+     */
+    private final boolean stripComment;
+    private final boolean encodeQuotes;
+    /**
+     * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
+     * becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
+     */
+    private final boolean alwaysMakeTags;
+
+    /**
+     * Default constructor.
+     */
+    public HTMLFilter() {
+        vAllowed = new HashMap<>();
+
+        final ArrayList<String> a_atts = new ArrayList<>();
+        a_atts.add("href");
+        a_atts.add("target");
+        vAllowed.put("a", a_atts);
+
+        final ArrayList<String> img_atts = new ArrayList<>();
+        img_atts.add("src");
+        img_atts.add("width");
+        img_atts.add("height");
+        img_atts.add("alt");
+        vAllowed.put("img", img_atts);
+
+        final ArrayList<String> no_atts = new ArrayList<>();
+        vAllowed.put("b", no_atts);
+        vAllowed.put("strong", no_atts);
+        vAllowed.put("i", no_atts);
+        vAllowed.put("em", no_atts);
+
+        vSelfClosingTags = new String[]{"img"};
+        vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
+        vDisallowed = new String[]{};
+        vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
+        vProtocolAtts = new String[]{"src", "href"};
+        vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
+        vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
+        stripComment = true;
+        encodeQuotes = true;
+        alwaysMakeTags = false;
+    }
+
+    /**
+     * Map-parameter configurable constructor.
+     *
+     * @param conf map containing configuration. keys match field names.
+     */
+    @SuppressWarnings("unchecked")
+    public HTMLFilter(final Map<String, Object> conf) {
+
+        assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+        assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+        assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+        assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+        assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+        assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+        assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+        assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+        vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
+        vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+        vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+        vDisallowed = (String[]) conf.get("vDisallowed");
+        vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+        vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+        vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+        vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+        stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+        encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+        alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+    }
+
+    // ---------------------------------------------------------------
+    // my versions of some PHP library functions
+    public static String chr(final int decimal) {
+        return String.valueOf((char) decimal);
+    }
+
+    public static String htmlSpecialChars(final String s) {
+        String result = s;
+        result = regexReplace(P_AMP, "&amp;", result);
+        result = regexReplace(P_QUOTE, "&quot;", result);
+        result = regexReplace(P_LEFT_ARROW, "&lt;", result);
+        result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
+        return result;
+    }
+
+    private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
+        Matcher m = regex_pattern.matcher(s);
+        return m.replaceAll(replacement);
+    }
+
+    // ---------------------------------------------------------------
+
+    private static boolean inArray(final String s, final String[] array) {
+        for (String item : array) {
+            if (item != null && item.equals(s)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void reset() {
+        vTagCounts.clear();
+    }
+
+    /**
+     * given a user submitted input String, filter out any invalid or restricted html.
+     *
+     * @param input text (i.e. submitted by a user) than may contain html
+     * @return "clean" version of input, with only valid, whitelisted html elements allowed
+     */
+    public String filter(final String input) {
+        reset();
+        String s = input;
+
+        s = escapeComments(s);
+
+        s = balanceHTML(s);
+
+        s = checkTags(s);
+
+        s = processRemoveBlanks(s);
+
+        // s = validateEntities(s);
+
+        return s;
+    }
+
+    public boolean isAlwaysMakeTags() {
+        return alwaysMakeTags;
+    }
+
+    public boolean isStripComments() {
+        return stripComment;
+    }
+
+    private String escapeComments(final String s) {
+        final Matcher m = P_COMMENTS.matcher(s);
+        final StringBuffer buf = new StringBuffer();
+        if (m.find()) {
+            final String match = m.group(1); // (.*?)
+            m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
+        }
+        m.appendTail(buf);
+
+        return buf.toString();
+    }
+
+    private String balanceHTML(String s) {
+        if (alwaysMakeTags) {
+            //
+            // try and form html
+            //
+            s = regexReplace(P_END_ARROW, "", s);
+            // 不追加结束标签
+            s = regexReplace(P_BODY_TO_END, "<$1>", s);
+            s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+        } else {
+            //
+            // escape stray brackets
+            //
+            s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
+            s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
+
+            //
+            // the last regexp causes '<>' entities to appear
+            // (we need to do a lookahead assertion so that the last bracket can
+            // be used in the next pass of the regexp)
+            //
+            s = regexReplace(P_BOTH_ARROWS, "", s);
+        }
+
+        return s;
+    }
+
+    private String checkTags(String s) {
+        Matcher m = P_TAGS.matcher(s);
+
+        final StringBuffer buf = new StringBuffer();
+        while (m.find()) {
+            String replaceStr = m.group(1);
+            replaceStr = processTag(replaceStr);
+            m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+        }
+        m.appendTail(buf);
+
+        // these get tallied in processTag
+        // (remember to reset before subsequent calls to filter method)
+        final StringBuilder sBuilder = new StringBuilder(buf.toString());
+        for (String key : vTagCounts.keySet()) {
+            for (int ii = 0; ii < vTagCounts.get(key); ii++) {
+                sBuilder.append("</").append(key).append(">");
+            }
+        }
+        s = sBuilder.toString();
+
+        return s;
+    }
+
+    private String processRemoveBlanks(final String s) {
+        String result = s;
+        for (String tag : vRemoveBlanks) {
+            if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
+                P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
+            }
+            result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+            if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
+                P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+            }
+            result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+        }
+
+        return result;
+    }
+
+    private String processTag(final String s) {
+        // ending tags
+        Matcher m = P_END_TAG.matcher(s);
+        if (m.find()) {
+            final String name = m.group(1).toLowerCase();
+            if (allowed(name)) {
+                if (!inArray(name, vSelfClosingTags)) {
+                    if (vTagCounts.containsKey(name)) {
+                        vTagCounts.put(name, vTagCounts.get(name) - 1);
+                        return "</" + name + ">";
+                    }
+                }
+            }
+        }
+
+        // starting tags
+        m = P_START_TAG.matcher(s);
+        if (m.find()) {
+            final String name = m.group(1).toLowerCase();
+            final String body = m.group(2);
+            String ending = m.group(3);
+
+            // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
+            if (allowed(name)) {
+                final StringBuilder params = new StringBuilder();
+
+                final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+                final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+                final List<String> paramNames = new ArrayList<>();
+                final List<String> paramValues = new ArrayList<>();
+                while (m2.find()) {
+                    paramNames.add(m2.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m2.group(3)); // (.*?)
+                }
+                while (m3.find()) {
+                    paramNames.add(m3.group(1)); // ([a-z0-9]+)
+                    paramValues.add(m3.group(3)); // ([^\"\\s']+)
+                }
+
+                String paramName, paramValue;
+                for (int ii = 0; ii < paramNames.size(); ii++) {
+                    paramName = paramNames.get(ii).toLowerCase();
+                    paramValue = paramValues.get(ii);
+
+                    // debug( "paramName='" + paramName + "'" );
+                    // debug( "paramValue='" + paramValue + "'" );
+                    // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
+
+                    if (allowedAttribute(name, paramName)) {
+                        if (inArray(paramName, vProtocolAtts)) {
+                            paramValue = processParamProtocol(paramValue);
+                        }
+                        params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\"");
+                    }
+                }
+
+                if (inArray(name, vSelfClosingTags)) {
+                    ending = " /";
+                }
+
+                if (inArray(name, vNeedClosingTags)) {
+                    ending = "";
+                }
+
+                if (ending == null || ending.length() < 1) {
+                    if (vTagCounts.containsKey(name)) {
+                        vTagCounts.put(name, vTagCounts.get(name) + 1);
+                    } else {
+                        vTagCounts.put(name, 1);
+                    }
+                } else {
+                    ending = " /";
+                }
+                return "<" + name + params + ending + ">";
+            } else {
+                return "";
+            }
+        }
+
+        // comments
+        m = P_COMMENT.matcher(s);
+        if (!stripComment && m.find()) {
+            return "<" + m.group() + ">";
+        }
+
+        return "";
+    }
+
+    private String processParamProtocol(String s) {
+        s = decodeEntities(s);
+        final Matcher m = P_PROTOCOL.matcher(s);
+        if (m.find()) {
+            final String protocol = m.group(1);
+            if (!inArray(protocol, vAllowedProtocols)) {
+                // bad protocol, turn into local anchor link instead
+                s = "#" + s.substring(protocol.length() + 1);
+                if (s.startsWith("#//")) {
+                    s = "#" + s.substring(3);
+                }
+            }
+        }
+
+        return s;
+    }
+
+    private String decodeEntities(String s) {
+        StringBuffer buf = new StringBuffer();
+
+        Matcher m = P_ENTITY.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.decode(match).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENTITY_UNICODE.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        buf = new StringBuffer();
+        m = P_ENCODE.matcher(s);
+        while (m.find()) {
+            final String match = m.group(1);
+            final int decimal = Integer.valueOf(match, 16).intValue();
+            m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+        }
+        m.appendTail(buf);
+        s = buf.toString();
+
+        s = validateEntities(s);
+        return s;
+    }
+
+    private String validateEntities(final String s) {
+        StringBuffer buf = new StringBuffer();
+
+        // validate entities throughout the string
+        Matcher m = P_VALID_ENTITIES.matcher(s);
+        while (m.find()) {
+            final String one = m.group(1); // ([^&;]*)
+            final String two = m.group(2); // (?=(;|&|$))
+            m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+        }
+        m.appendTail(buf);
+
+        return encodeQuotes(buf.toString());
+    }
+
+    private String encodeQuotes(final String s) {
+        if (encodeQuotes) {
+            StringBuffer buf = new StringBuffer();
+            Matcher m = P_VALID_QUOTES.matcher(s);
+            while (m.find()) {
+                final String one = m.group(1); // (>|^)
+                final String two = m.group(2); // ([^<]+?)
+                final String three = m.group(3); // (<|$)
+                // 不替换双引号为&quot;,防止json格式无效 regexReplace(P_QUOTE, "&quot;", two)
+                m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
+            }
+            m.appendTail(buf);
+            return buf.toString();
+        } else {
+            return s;
+        }
+    }
+
+    private String checkEntity(final String preamble, final String term) {
+
+        return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&amp;" + preamble;
+    }
+
+    private boolean isValidEntity(final String entity) {
+        return inArray(entity, vAllowedEntities);
+    }
+
+    private boolean allowed(final String name) {
+        return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+    }
+
+    private boolean allowedAttribute(final String name, final String paramName) {
+        return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+    }
+}

+ 208 - 0
src/main/java/com/hz/employmentsite/util/ip/HttpUtils.java

@@ -0,0 +1,208 @@
+package com.hz.employmentsite.util.ip;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.X509Certificate;
+
+/**
+ * 通用http发送方法
+ *
+ * @author ruoyi
+ */
+public class HttpUtils {
+    private static final Logger log = LoggerFactory.getLogger(HttpUtils.class);
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url 发送请求的 URL
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url) {
+        return sendGet(url, "");
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url   发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param) {
+        return sendGet(url, param, "UTF-8");
+    }
+
+    /**
+     * 向指定 URL 发送GET方法的请求
+     *
+     * @param url         发送请求的 URL
+     * @param param       请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @param contentType 编码类型
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendGet(String url, String param, String contentType) {
+        StringBuilder result = new StringBuilder();
+        BufferedReader in = null;
+        try {
+            String urlNameString = (param != null || "".equals(param.trim())) ? url + "?" + param : url;
+            log.info("sendGet - {}", urlNameString);
+            URL realUrl = new URL(urlNameString);
+            URLConnection connection = realUrl.openConnection();
+            connection.setRequestProperty("accept", "*/*");
+            connection.setRequestProperty("connection", "Keep-Alive");
+            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            connection.connect();
+            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            log.info("recv - {}", result);
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
+        } finally {
+            try {
+                if (in != null) {
+                    in.close();
+                }
+            } catch (Exception ex) {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * 向指定 URL 发送POST方法的请求
+     *
+     * @param url   发送请求的 URL
+     * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。
+     * @return 所代表远程资源的响应结果
+     */
+    public static String sendPost(String url, String param) {
+        PrintWriter out = null;
+        BufferedReader in = null;
+        StringBuilder result = new StringBuilder();
+        try {
+            String urlNameString = url;
+            log.info("sendPost - {}", urlNameString);
+            URL realUrl = new URL(urlNameString);
+            URLConnection conn = realUrl.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+            out = new PrintWriter(conn.getOutputStream());
+            out.print(param);
+            out.flush();
+            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            log.info("recv - {}", result);
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
+        } finally {
+            try {
+                if (out != null) {
+                    out.close();
+                }
+                if (in != null) {
+                    in.close();
+                }
+            } catch (IOException ex) {
+                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
+            }
+        }
+        return result.toString();
+    }
+
+    public static String sendSSLPost(String url, String param) {
+        StringBuilder result = new StringBuilder();
+        String urlNameString = url + "?" + param;
+        try {
+            log.info("sendSSLPost - {}", urlNameString);
+            SSLContext sc = SSLContext.getInstance("SSL");
+            sc.init(null, new TrustManager[]{new TrustAnyTrustManager()}, new java.security.SecureRandom());
+            URL console = new URL(urlNameString);
+            HttpsURLConnection conn = (HttpsURLConnection) console.openConnection();
+            conn.setRequestProperty("accept", "*/*");
+            conn.setRequestProperty("connection", "Keep-Alive");
+            conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
+            conn.setRequestProperty("Accept-Charset", "utf-8");
+            conn.setRequestProperty("contentType", "utf-8");
+            conn.setDoOutput(true);
+            conn.setDoInput(true);
+
+            conn.setSSLSocketFactory(sc.getSocketFactory());
+            conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
+            conn.connect();
+            InputStream is = conn.getInputStream();
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String ret = "";
+            while ((ret = br.readLine()) != null) {
+                if (ret != null && !"".equals(ret.trim())) {
+                    result.append(new String(ret.getBytes("ISO-8859-1"), "utf-8"));
+                }
+            }
+            log.info("recv - {}", result);
+            conn.disconnect();
+            br.close();
+        } catch (ConnectException e) {
+            log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
+        } catch (SocketTimeoutException e) {
+            log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
+        } catch (IOException e) {
+            log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
+        } catch (Exception e) {
+            log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
+        }
+        return result.toString();
+    }
+
+    private static class TrustAnyTrustManager implements X509TrustManager {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return new X509Certificate[]{};
+        }
+    }
+
+    private static class TrustAnyHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return true;
+        }
+    }
+}

+ 163 - 0
src/main/java/com/hz/employmentsite/util/ip/IpUtils.java

@@ -0,0 +1,163 @@
+package com.hz.employmentsite.util.ip;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * 获取IP方法
+ *
+ * @author ruoyi
+ */
+public class IpUtils {
+    public static String getIpAddr(HttpServletRequest request) {
+        if (request == null) {
+            return "unknown";
+        }
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Forwarded-For");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("WL-Proxy-Client-IP");
+        }
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getHeader("X-Real-IP");
+        }
+
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            ip = request.getRemoteAddr();
+        }
+        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : EscapeUtil.clean(ip);
+    }
+
+    public static boolean internalIp(String ip) {
+        byte[] addr = textToNumericFormatV4(ip);
+        return internalIp(addr) || "127.0.0.1".equals(ip);
+    }
+
+    private static boolean internalIp(byte[] addr) {
+        if (addr == null || addr.length < 2) {
+            return true;
+        }
+        final byte b0 = addr[0];
+        final byte b1 = addr[1];
+        // 10.x.x.x/8
+        final byte SECTION_1 = 0x0A;
+        // 172.16.x.x/12
+        final byte SECTION_2 = (byte) 0xAC;
+        final byte SECTION_3 = (byte) 0x10;
+        final byte SECTION_4 = (byte) 0x1F;
+        // 192.168.x.x/16
+        final byte SECTION_5 = (byte) 0xC0;
+        final byte SECTION_6 = (byte) 0xA8;
+        switch (b0) {
+            case SECTION_1:
+                return true;
+            case SECTION_2:
+                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
+                    return true;
+                }
+            case SECTION_5:
+                switch (b1) {
+                    case SECTION_6:
+                        return true;
+                }
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * 将IPv4地址转换成字节
+     *
+     * @param text IPv4地址
+     * @return byte 字节
+     */
+    public static byte[] textToNumericFormatV4(String text) {
+        if (text.length() == 0) {
+            return null;
+        }
+
+        byte[] bytes = new byte[4];
+        String[] elements = text.split("\\.", -1);
+        try {
+            long l;
+            int i;
+            switch (elements.length) {
+                case 1:
+                    l = Long.parseLong(elements[0]);
+                    if ((l < 0L) || (l > 4294967295L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
+                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 2:
+                    l = Integer.parseInt(elements[0]);
+                    if ((l < 0L) || (l > 255L)) {
+                        return null;
+                    }
+                    bytes[0] = (byte) (int) (l & 0xFF);
+                    l = Integer.parseInt(elements[1]);
+                    if ((l < 0L) || (l > 16777215L)) {
+                        return null;
+                    }
+                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
+                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 3:
+                    for (i = 0; i < 2; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    l = Integer.parseInt(elements[2]);
+                    if ((l < 0L) || (l > 65535L)) {
+                        return null;
+                    }
+                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
+                    bytes[3] = (byte) (int) (l & 0xFF);
+                    break;
+                case 4:
+                    for (i = 0; i < 4; ++i) {
+                        l = Integer.parseInt(elements[i]);
+                        if ((l < 0L) || (l > 255L)) {
+                            return null;
+                        }
+                        bytes[i] = (byte) (int) (l & 0xFF);
+                    }
+                    break;
+                default:
+                    return null;
+            }
+        } catch (NumberFormatException e) {
+            return null;
+        }
+        return bytes;
+    }
+
+    public static String getHostIp() {
+        try {
+            return InetAddress.getLocalHost().getHostAddress();
+        } catch (UnknownHostException e) {
+        }
+        return "127.0.0.1";
+    }
+
+    public static String getHostName() {
+        try {
+            return InetAddress.getLocalHost().getHostName();
+        } catch (UnknownHostException e) {
+        }
+        return "未知";
+    }
+}

+ 20 - 5
src/main/resources/mapping/SysLogMapper.xml

@@ -7,6 +7,7 @@
     <result column="PageUrl" jdbcType="VARCHAR" property="pageUrl" />
     <result column="ActionName" jdbcType="VARCHAR" property="actionName" />
     <result column="UserID" jdbcType="VARCHAR" property="userID" />
+    <result column="IPAddress" jdbcType="VARCHAR" property="IPAddress" />
     <result column="LogTime" jdbcType="TIMESTAMP" property="logTime" />
   </resultMap>
   <sql id="Example_Where_Clause">
@@ -68,7 +69,7 @@
     </where>
   </sql>
   <sql id="Base_Column_List">
-    LogID, PageName, PageUrl, ActionName, UserID, LogTime
+    LogID, PageName, PageUrl, ActionName, UserID, IPAddress, LogTime
   </sql>
   <select id="selectByExample" parameterType="com.hz.employmentsite.model.SysLogExample" resultMap="BaseResultMap">
     select
@@ -102,11 +103,11 @@
   </delete>
   <insert id="insert" parameterType="com.hz.employmentsite.model.SysLog">
     insert into sys_log (LogID, PageName, PageUrl, 
-      ActionName, UserID, LogTime
-      )
+      ActionName, UserID, IPAddress, 
+      LogTime)
     values (#{logID,jdbcType=VARCHAR}, #{pageName,jdbcType=VARCHAR}, #{pageUrl,jdbcType=VARCHAR}, 
-      #{actionName,jdbcType=VARCHAR}, #{userID,jdbcType=VARCHAR}, #{logTime,jdbcType=TIMESTAMP}
-      )
+      #{actionName,jdbcType=VARCHAR}, #{userID,jdbcType=VARCHAR}, #{IPAddress,jdbcType=VARCHAR}, 
+      #{logTime,jdbcType=TIMESTAMP})
   </insert>
   <insert id="insertSelective" parameterType="com.hz.employmentsite.model.SysLog">
     insert into sys_log
@@ -126,6 +127,9 @@
       <if test="userID != null">
         UserID,
       </if>
+      <if test="IPAddress != null">
+        IPAddress,
+      </if>
       <if test="logTime != null">
         LogTime,
       </if>
@@ -146,6 +150,9 @@
       <if test="userID != null">
         #{userID,jdbcType=VARCHAR},
       </if>
+      <if test="IPAddress != null">
+        #{IPAddress,jdbcType=VARCHAR},
+      </if>
       <if test="logTime != null">
         #{logTime,jdbcType=TIMESTAMP},
       </if>
@@ -175,6 +182,9 @@
       <if test="row.userID != null">
         UserID = #{row.userID,jdbcType=VARCHAR},
       </if>
+      <if test="row.IPAddress != null">
+        IPAddress = #{row.IPAddress,jdbcType=VARCHAR},
+      </if>
       <if test="row.logTime != null">
         LogTime = #{row.logTime,jdbcType=TIMESTAMP},
       </if>
@@ -190,6 +200,7 @@
       PageUrl = #{row.pageUrl,jdbcType=VARCHAR},
       ActionName = #{row.actionName,jdbcType=VARCHAR},
       UserID = #{row.userID,jdbcType=VARCHAR},
+      IPAddress = #{row.IPAddress,jdbcType=VARCHAR},
       LogTime = #{row.logTime,jdbcType=TIMESTAMP}
     <if test="example != null">
       <include refid="Update_By_Example_Where_Clause" />
@@ -210,6 +221,9 @@
       <if test="userID != null">
         UserID = #{userID,jdbcType=VARCHAR},
       </if>
+      <if test="IPAddress != null">
+        IPAddress = #{IPAddress,jdbcType=VARCHAR},
+      </if>
       <if test="logTime != null">
         LogTime = #{logTime,jdbcType=TIMESTAMP},
       </if>
@@ -222,6 +236,7 @@
       PageUrl = #{pageUrl,jdbcType=VARCHAR},
       ActionName = #{actionName,jdbcType=VARCHAR},
       UserID = #{userID,jdbcType=VARCHAR},
+      IPAddress = #{IPAddress,jdbcType=VARCHAR},
       LogTime = #{logTime,jdbcType=TIMESTAMP}
     where LogID = #{logID,jdbcType=VARCHAR}
   </update>

+ 1 - 1
src/main/resources/mapping/cquery/SysLogCQuery.xml

@@ -2,7 +2,7 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 <mapper namespace="com.hz.employmentsite.mapper.cquery.SysLogCQuery">
     <select id="selectLogList" resultType="com.hz.employmentsite.vo.system.LogModel">
-        select l.logID,l.logID,l.pageUrl,l.actionName,l.userID,l.logTime,us.name as userName,us.loginID,l.pageName
+        select l.logID,l.pageUrl,l.actionName,l.userID,l.logTime,us.name as userName,us.loginID,l.pageName,l.IPAddress
         from sys_log l
         inner join sys_user us on l.userID = us.userID
         where 1=1

+ 1 - 0
vue/src/views/system/log/index.vue

@@ -86,6 +86,7 @@ export default defineComponent({
         isDisabled: true
       },
       {title: '登录账号', dataIndex: 'loginID', key: 'loginID', align: "center"},
+      {title: '访问IP', dataIndex: 'ipaddress', key: 'ipaddress', align: "center"},
       {title: '姓名', dataIndex: 'userName', key: 'userName',align:"center"},
       {title: '操作页面', dataIndex: 'pageName', key: 'pageName',align:"center"},
       {