前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

1、Http协议

1.1、http连接

     HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。    

     1)在HTTP 1.0(短连接)中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。

     2)在HTTP 1.1(长连接)中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。

     由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,为了让它变成长连接,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的 做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客 户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

1.2、在浏览器中输入网址发生了什么

1、解析Web页面的URL,得到Web服务器的域名,查询本地的DNS缓存,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,以找到域名对应的主机IP地址(若有,则跳到4)查询远程域名根DNS,找到IP地址

2、通过DNS服务器获得Web服务器的IP地址

3、与Web服务器建立TCP连接

4、与Web服务器建立HTTP连接,向远程IP地址的服务器发送请求

5、从Web服务器获得URL指定的文档

6、浏览器解释页面文档,并显示在屏幕

1.3、TCP三次握手

所谓三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送3个包

WX20190208-213200@2x

1.3.1、连接过程

SYN: (同步序列编号,Synchronize Sequence Numbers)

ACK: (确认编号,Acknowledgement Number)

FIN: (结束标志,FINish)

1.3.1.1、服务器和客户端初始状态 :CLOSED

1.3.1.2、服务器创建Socket后开始监听:LISTEN

1.3.1.4、第一次握手:客户端请求建立连接,客户端的状态:SYN_SENT

客户端请求建立连接,向服务器发送SYN报文,客户端的状态变成:SYN_SENT

SYN=1,seq=x 

SYN:置为1, 表示需要建立TCP连接     
seq:序列号,是由发送端随机生成的

1.3.1.5、第二次握手:服务端发起,服务器状态:SYN_RECEIVED

服务器收到客户端的报文后向客户端发送ACK和SYN报文,此时服务器的状态变为 SYN_RCVD

SYN=1,ACK=x+1,seq=y   

SYN :置为1   
ACK: ACK字段数值是在客户端发送过来的序列号seq的基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP建立请求已得到验证。ck加1可以理解为是确认和谁建立连接
seq: seq序列号,是由服务端端随机生成的

1.3.1.6、第三次握手:客户端发起,客户端和服务端状态:ESTABLISHED

客户端收到ACKSYN,就向服务器发送ACK,客户端状态变为ESTABLISHED,服务器端收到客户端的ACK后变为ESTABLISHED。此时3次握手完成,连接建立!

SYN=1,ACK=y+1,seq=x+1  

SYN :置为1      
ACK: ACK字段数值是在服务端发送过来的序列号seq的基础上加1进行回复,以便服务端收到信息时,知晓自己的TCP建立请求已得到验证。  
seq:客户端收到服务端发送的TCP建立验证请求后,会使自己的序列号加1表示,

1.3.2、状态解释

LISTEN:等待从任何远端TCP 和端口的连接请求。

SYN_SENT:发送完一个连接请求后等待一个匹配的连接请求。

SYN_RECEIVED:发送连接请求并且接收到匹配的连接请求以后等待连接请求确认。

ESTABLISHED:表示一个打开的连接,接收到的数据可以被投递给用户。连接的数据传输阶段的正常状态。

1.3.3、SYN攻击

1.3.3.1、攻击原理

1 、Client 伪造大量的虚假ip,向Server发送SYN包

2、Server在接收到SYN包后,会返回响应,并进入 SYN_RECV 状态,等待客户端的确认、但是伪造的ip肯定不会给予响应,于是Server 以为数据包丢失,不断重发,直到超时

1.3.3.2、危害

这些伪造的SYN包会长期占用未连接队列,导致后来真实的ip无法加入队列,从而被丢弃,引起网络拥堵甚至网络瘫痪

1.3.3.3、如何确认是SYN攻击

当服务器上有大量的半连接且ip为随机的,可以确认是SYN攻击

netstat -nap | grep SYN_RECV

1.3.3.4、阻止

修改tcp协议实现。主要方法有SynAttackProtect保护机制、SYN cookies技术、增加最大半连接和缩短超时时间等

1.3.4、为什么是“三次握手?二次握手有什么问题?

为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

谢希仁版《计算机网络》中的例子是这样的,“已失效的连接请求报文段”的产生在这样一种情况下:

1、client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server

2、本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。

3、假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。

4、由于现在client 并没有发出建立连接的请求,因此不会理睬server 的确认,也不会向 server 发送数据。

5、但 server 却以为新的运输连接已经建立,并一直等待client发来数据。这样server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”

如果采用了三次握手,由于客户端并没有发送新的连接请求,服务器端接收到的只是一个失效的报文段,所以客户端并不会理踩服务器端发送的确认,也不会向服务器端发送确认,此时服务器端就会知道客户端并没有请求建立连接,连接不会建立,也不会一直等待(超时断开)。

1.4、TCP四次挥手

1.4.1、握手过程

由于TCP连接是全双工的,断开连接会比建立连接麻烦一点点

1.4.1.1、第一次挥手 客户端发起,客户端状态为:FIN_WAIT1

客户端先向服务器发送FIN报文,请求断开连接,其状态变为。FIN_WAIT1

FIN=1,seq=x

FIN:置为1
seq:发送端随机生成的

1.4.1.2、第二次挥手 服务端发起,服务端的状态:CLOSE_WAIT

服务端收到客户端请求断开的报文后,向客户端回复报文,此时服务器的状态变为:CLOSE_WAIT

FIN=1 ACK=x+1 seq=y  

FIN:置为1
ACK:字段,ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP断开请求已经得到验证。
seq:服务端随机生成  

1.4.1.3、第三次挥手 服务端发起,服务端状态:LAST_ACK

服务端在回复完客户端的TCP断开请求后,不会马上进行TCP连接的断开,服务端会先确保断开前,所有传输到A的数据是否已经传输完毕,一旦确认传输数据完毕就会继续回复报文
FIN=1,ACK=x+1,seq=z   

FIN:置为1
ACK:字段,ACK字段数值是在客户端发过来的seq序列号基础上加1进行回复,以便客户端收到信息时,知晓自己的TCP断开请求已经得到验证。
seq:服务端随机生成  

1.4.1.4、第四次挥手 客户端的发起,等待收2次报文

客户端收到服务端第一次响应后变成·FIN_WAIT2状态,

此时连接已经断开了一半了。如果服务器还有数据要发送给客户端,就会服务端确认完毕,客户端再次接受到报文的时候;客户端状态变成 TIME_WAIT状态

再过了2MSL长的时间后进入CLOSED状态。服务器收到客户端的ACK就进入CLOSED状态,

至此,还有一个状态没有出来:CLOSING状态:等待远端TCP 的连接终止请求确认。客户端发送了FIN,但是没有收到服务器的ACK,却收到了服务器的FIN,这种情况发生在服务器发送的ACK丢包的时候,因为网络传输有时会有意外。

FIN=1,ACK=z+1,seq=h

1.4.2、状态解释

FIN_WAIT_1:等待远端TCP 的连接终止请求,或者等待之前发送的连接终止请求的确认。


CLOSE_WAIT:等待本地用户的连接终止请求。

CLOSING:等待远端TCP 的连接终止请求确认。客户端发送了FIN,但是没有收到服务器的ACK,却收到了服务器的FIN,这种情况发生在服务器发送的ACK丢包的时候,因为网络传输有时会有意外。 

LAST_ACK:等待先前发送给远端TCP 的连接终止请求的确认(包括它字节的连接终止请求的确认)

FIN_WAIT_2:等待远端TCP 的连接终止请求。

TIME_WAIT:等待足够的时间过去以确保远端TCP 接收到它的连接终止请求的确认。
TIME_WAIT 两个存在的理由:
          1.可靠的实现tcp全双工连接的终止;
          2.允许老的重复分节在网络中消逝。

CLOSED:不在连接状态(这是为方便描述假想的状态,实际不存在):

2、Request藏了什么东西

2.1、路径获取

2.1.1、request.getRequestURL

获取全部路径

log.info(request.getRequestURL().toString()); ////全部路径,不带参数 http://localhost:8081/healerjean/youhui/web/authorize

2.1.2、request.getContextPath()

2.2.3、request.getRequestURI()

//4、当然我们的Springboot项目中是没有用项目的名字的,下面来源于网络)  
System.out.println(request.getContextPath()); ///可返回站点的根路径。也就是项目的名字   打印结果:/news  
System.out.println(request.getServletPath()); //打印结果:/main/list.jsp  
System.out.println(request.getRequestURI());  //打印结果:/news/main/list.jsp  

我的项目中因为没有项目的名字所以下面二者显示的是一样的
log.info(request.getRequestURI());  //我的项目中没有项目的名字 healerjean/youhui/web/authorize
log.info(request.getServletPath());      //healerjean/youhui/web/authorize

2.2、request.getServerName :获取服务器的 主机名

log.info(request.getServerName());       //localhost    test.healerjean.cn

2.3、request.getServerPort() :获取Htpp端口

log.info(request.getServerPort()+"");        //8081  80  

2.4、request.getLocalPort() :获取本机端口

获取本机端口号(第二个是阿里云的)

log.info(request.getLocalPort()+"");        //8081  8086

2.5、request.getHeader("host")

获取域名+端口(主机名加端口,80不显示)

log.info(request.getHeader("host"));  //localhost:8081  test.healerjean.cn

2.6、request.getHeader( "referer" )

获取来源于上一次网页的url

如果是直接根据地址访问的则则为null
log.info(request.getHeader( "referer" )); //进入这个地址之前是从那个连接过来的,防盗连接使用

2.7、request.getMethod: 获取请求方法

request.getMethod() //POST 、GET

2.8、request.getContentType()/ContentType

Content-Type是指http/https发送信息至服务器时的内容编码类型,contentType用于表明发送数据流的类型,服务器根据编码类型使用特定的解析方式,获取数据流中的数据。

1、GET和POST默认都是application/x-www-form-unlencoded, 一般会指定,有时候不指定也可能遇到获取为null的情况

2、使用表单上传文件时,必须指定表单的 enctype属性值为 multipart/form-data. 请求体被分割成多部分,每部分使用 –boundary分割;

3、在网络请求中,常用的Content-Type有如下:

text/html, 
text/plain, 
text/css, 
text/javascript, 
image/jpeg, 
image/png, 
image/gif, 
以上都是常见的页面资源类型。



application/x-www-form-urlencoded,  
multipart/form-data, 
application/json, 
application/xml
这四个是ajax的请求,表单提交或上传文件的常用的资源类型。

2.9、request.getHeaderNames 获取请求头

2.9.1、遍历

request.getHeaderNames()

Enumeration headerNames=request.getHeaderNames();
   while(headerNames.hasMoreElements()){
       String paraName=(String)headerNames.nextElement();
       System.out.println(paraName+": "+request.getHeader(paraName));
   }

2.9.2、GET/POST返回的不同结果

2.9.2.1、GET

host: localhost:8081
connection: keep-alive
cache-control: max-age=0
upgrade-insecure-requests: 1
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
accept-encoding: gzip, deflate, br
accept-language: en,zh-CN;q=0.9,zh;q=0.8
cookie: _Token=9UUife5cUeoOLO4AOaExoA==; _aToken=ceshib; ab.storage.userId.8f0d3f99-1e3f-4444-bb6c-f73da32cc97c=%7B%22g%22%3A%22437277%22%2C%22c%22%3A1546077272288%2C%22l%22%3A1546077272288%7D; ab.storage.deviceId.8f0d3f99-1e3f-4444-bb6c-f73da32cc97c=%7B%22g%22%3A%229a4b0cd9-da59-9c0e-5aee-a8356e4ea5dd%22%2C%22c%22%3A1546077272296%2C%22l%22%3A1546077272296%7D; ab.storage.sessionId.8f0d3f99-1e3f-4444-bb6c-f73da32cc97c=%7B%22g%22%3A%22c137279a-3f33-6df2-b1fa-189f47793f93%22%2C%22e%22%3A1546079072315%2C%22c%22%3A1546077272293%2C%22l%22%3A1546077272315%7D; CONTAINERID=7485f72559e40cd7d5f09b1347a03d3a73e9a0be752d74c0f76412a132033784|XC3gl|XC3gW; JSESSIONID=13A2286D04353AD554B27FF77DF8CABA

2.9.2.1、POST

host: localhost:8081
connection: keep-alive
content-length: 453
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
cache-control: no-cache
origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
postman-token: bfd0da34-bed9-b903-e852-5656de78883c
token: ZDjQ7mDEwlU7t6yEcAK2Xw==
content-type: multipart/form-data; boundary=----WebKitFormBoundaryaIFtRs1mZ4nS9I7j
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en,zh-CN;q=0.9,zh;q=0.8

3.10、获取请求数据

3.10.1、request.getQueryString(): 访问的参数

只能用于GET

log.info(request.getQueryString()); //参数 ,只适用于get

age=1&email=healerjewa%40gmail.com&name=healerjean&phone=17611115555

3.10.2、request.getParameterNames

不适用contentType为multipart/form-data

获取参数的名字不管你代码里面接收不接收反正我这里是可以获取到的
request.getParameterNames()

   Enumeration enu=request.getParameterNames();
   while(enu.hasMoreElements()){
       String paraName=(String)enu.nextElement();
       System.out.println(paraName+": "+request.getParameter(paraName));
   }
        
参数如下
accessDate: 2019-01-02 00:00:00
pageNum: 1
pageSize: 10
post: Healerjean


3.10.3、request.getParameterMap

获取参数map值: 不适用contentType为multipart/form-data

request.getParameterMap()


Map<String, String[]> parameterMap = request.getParameterMap();
log.info(JsonUtils.toJson(parameterMap));
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
  System.out.println(entry.getKey());
  for (String string : entry.getValue()) {
      System.out.println(string);
  }
  System.out.println("----------------");
}

3.10.4、request.getParameter()request.getInputStream() \ request.getReader()

3.10.4.1、getParameter()

getpost都可以使用,但如果是post请求要根据表单提交数据的编码方式来确定能否使用.当编码方式是application/x- www-form-urlencoded时才能使用(默认).这种编码方式虽然简单,但对于传输大块的二进制数据显得力不从心

3.10.5.2、getInputStream()getReader()

对于传输大块的二进制数这类数据,浏览器采用了另一种编码方式(“multipart/form-data”),这时就需要使用下面的两种方法. getInputStream()getReader()

3.10.5.3、3中方式的使用冲突

3.10.5.3.1、冲突介绍

request.getParameter()、 request.getInputStream()request.getReader()这三种方法是有冲突的,因为流只能被读一次。

1、 当form表单内容采用 enctype=application/x-www-form-urlencoded编码时,先通过调用request.getParameter() 方法得到参数后,再调用request.getInputStream()request.getReader()已经得不到流中的内容,因为在调用 request.getParameter()时系统可能对表单中提交的数据以流的形式读了一次,反之亦然。

2、当form表单内容采用enctype=multipart/form-data编码时,即使先调用request.getParameter()也得不到数据,所以这时调用request.getParameter()方法对 request.getInputStream()或request.getReader()没有冲突,即使已经调用了 request.getParameter()方法也可以通过调用request.getInputStream()request.getReader()得 到表单中的数据,

3、request.getInputStream()request.getReader()在同一个响应中是不能混合使用的,如果混合使用就会抛异常。

3.10.5.3.2、让流可以被多次调用、在最后执行的过滤器中
package com.hlj.proj.config.filter;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author HealerJean
 * @ClassName RequestFilter
 * @date 2019-11-25  21:48.
 * @Description
 */
@Slf4j
public class RequestFilter implements Filter {

    private FilterConfig filterConfig;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws
            IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        filterChain.doFilter(new ReuqestFiterHttpServletRequestWrapper(request), servletResponse);
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

package com.hlj.proj.config.filter;


import org.apache.commons.io.IOUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;


public class ReuqestFiterHttpServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] body;

    /**
     * 预先出初始化数据
     */
    public ReuqestFiterHttpServletRequestWrapper(HttpServletRequest servletRequest) throws IOException {
        super(servletRequest);
        InputStream inputStream = servletRequest.getInputStream();
        body = IOUtils.toByteArray(inputStream);
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }


    @Override
    public ServletInputStream getInputStream() {
        ByteArrayInputStream bis = new ByteArrayInputStream(body);
        return new ServletInputStream() {
            @Override
            public int read() {
                return bis.read();
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

        };
    }


}

3、Session和Cookie

3.1、跨域定位

URL 说明 是否允许通信
http://www.a.com/a.js 
http://www.a.com/b.js
同一域名下 允许
http://www.a.com/lab/a.js 
http://www.a.com/script/b.js
同一域名下不同文件夹 允许
http://www.a.com:8000/a.js 
http://www.a.com/b.js
同一域名,不同端口 不允许
http://www.a.com/a.js 
https://www.a.com/b.js
同一域名,不同协议 不允许
http://www.a.com/a.js 
http://70.32.92.74/b.js
域名和域名对应ip 不允许
http://www.a.com/a.js 
http://script.a.com/b.js
主域相同,子域不同 不允许
http://www.a.com/a.js 
http://a.com/b.js
同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js 
http://www.a.com/b.js
不同域名 不允许

3.1、Session

3.1.1、问题汇总

3.1.1.1、 一个域名下项目session不能同时存在

之前做过一个Nginx,Tomcat,session共享,当时用了的是一个域名,也就是常见的localhost,当时没有考虑过一个域名下的session是不是同一个,但是端口不同,得到的正常情况下sessionId不是同一个。通过配置tomcat和项目,最后得到是一个sessionId。其实现在我才知道,如果没有配置共享session,本来一个localhost,不同端口,如果开启项目的话,每次刷新不同的端口项目,session变了。那是因为当前的端口将之前的端口的sessionId被替换掉了。但是之前的端口的sessionId再刷新的话,就又会变了。

接着说sso,我还是用了同一个localhost,发现,单点是可以登录,但是只能登录一个客户端。这就偶是3中强调的一个域名session是同一个,如果登录另外一个。通过sso就会将之前的session给干掉。只留下当前登录的那个客户端。我这里用的是sso使用的是ticket验证,不可以共用一个sessionId的。所以请看下面的第一个

3.1.2、session存储和使用

存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。 session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。 Session的默认失效时间是30分钟,

3.1.3、 从session中获取数据

HttpSession session = request.getSession();
 System.out.println(session.getId());


UserDTO user = session.getAttribute("USER"):

3.1.4、遍历Session

//获取session
HttpSession session   =   request.getSession();
// 获取session中所有的键值
Enumeration<String> attrs = session.getAttributeNames();
    // 遍历attrs中的
while(attrs.hasMoreElements()){
    // 获取session键值
    String name = attrs.nextElement().toString();
    // 根据键值取session中的值
    Object vakue = session.getAttribute(name);
    // 打印结果
    System.out.println("------" + name + ":" + vakue +"--------\n");
}

sessionName::_const_cas_assertion_-value:org.jasig.cas.client.validation.AssertionImpl@12d08532

3.4、Cookie

3.4.1、遍历Cookie


Cookie[] cookies = request.getCookies();
String  JSESSIONID = null;
String loginSessionId = null ;
for(Cookie cookie:cookies){
    cookie.getValue() ;
}

3.5、结合单点登录看

看另一篇文章,单点登录原理

工具类

1、Ip工具类

package com.hlj.utils.ip;

import com.hlj.utils.ExceptionLogUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @Desc:
 * @Author HealerJean
 * @Date 2018/7/4  下午2:36.
 */
public class IpUtil {

    public static String getIp(){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(request==null)return null;
        String ip = request.getHeader("x-forwarded-for");
        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();
        }
        if(ip.toLowerCase().contains("x-forwarded-for")){
            String ip_temp = "";
            if(ip.contains(":") && ip.split(":").length>0 && !ip.endsWith(":")){
                ip_temp = ip.split(":")[1];
                if(StringUtils.isBlank(ip_temp)) ip_temp = ip.substring(0,ip.lastIndexOf("X-Forwarded-For"));
            }else{
                ip_temp = ip.substring(0,ip.lastIndexOf("X-Forwarded-For"));
            }
            ip = ip_temp.replaceAll(" ","");
        }
        if (ip.indexOf(",") > -1) {
            String ip_temp = ip.split(",")[0];
            ip_temp = ip_temp.replaceAll(" ", "");
            if(ip_temp.startsWith("10.") && ip.split(",").length>1){
                ip = ip.split(",")[1];
                ip = ip.replaceAll(" ", "");
            }else ip = ip_temp;
        }
        return ip;
    }


      /**
     * 获取调用的http/https+ 域名 + 端口
     * @param urlTarget
     * @return
     */
    public static String getDomainAndPort(String urlTarget)
    {
        //跳转到对应的回调地址
        String domain = "";
        try {
            URL url = new URL(urlTarget);
            String host = url.getHost();
            int port = url.getPort();
            String s = url.toString();
            domain = s.substring(0,s.indexOf(host)+host.length());
            if(port != -1) {
                domain = domain + ":" + port;
            }
        } catch (MalformedURLException e) {
            log.info("获取域名失败");
        }
        return domain ;
    }



    /**
     * 获取服务器ip
     * @return
     */
    public static  String getHostIp(){
        try {
            InetAddress ia2 = InetAddress.getLocalHost();
            return  ia2.getHostAddress();
        } catch (UnknownHostException e) {
            ExceptionLogUtils.log(e,IpUtil.class);
            return  null ;
        }
    }

    /**
     * 根据域名获取ip
     * www.baidu.com
     * @param url
     * @return
     */
    public  static String getIpByUrl(String url) {
        try {
            InetAddress ia2 = InetAddress.getByName(url);
            return ia2.getHostAddress();
        } catch (UnknownHostException e) {
            ExceptionLogUtils.log(e,IpUtil.class );
            return  null;
        }
    }

    public static IPEntry getAddress(String ip){
        return  IPSeeker.getInstance().getAddress(ip);
    }

    public static void main(String[] args) {
        System.out.println(getAddress("106.39.75.134"));
    }

}

2、Request 工具类


import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 静态获取HttpServletRequest 和 session的方法
 * 要使用此类需要在web.xml注册org.springframework.web.context.request.RequestContextListener
 */
public class RequestHolder {

    public static HttpSession getSession() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        return request.getSession();
    }

    public static HttpServletRequest getRequest() {
        return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static HttpServletResponse getResponse() {
        return ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
    }

}

3、Cookie工具类

package com.duodian.youhui.admin.utils.cookie;

import com.duodian.youhui.admin.utils.RequestHolder;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Desc: Cookie 工具类
 * @Date:  2018/9/12 下午1:02.
 */

public class CookieHelper {


    public static void setCookie(String key,String value,Integer seconds){
        HttpServletResponse response = RequestHolder.getResponse();
        Cookie add = new Cookie(key,value);
        add.setMaxAge(seconds);
        add.setPath("/");
        response.addCookie(add);
    }

    public static String getCookieValue(String key){
        HttpServletRequest request = RequestHolder.getRequest();
        Cookie[] cookies = request.getCookies();
        if (cookies == null){
            return null;
        }
        for (Cookie cookie : cookies){
            if (StringUtils.equals(cookie.getName(), key)){
                return cookie.getValue();
            }
        }
        return null;
    }

    public static void clearCookie(String key){
        HttpServletResponse response = RequestHolder.getResponse();
        Cookie clear = new Cookie(key,"");
        clear.setMaxAge(0);
        clear.setComment("清除cookie");
        clear.setPath("/");
        response.addCookie(clear);
    }


}

3、Session工具

package com.duodian.admore.home.utils;

import com.duodian.admore.constants.AppConstants;
import com.duodian.admore.core.spring.RequestHolder;
import com.duodian.admore.entity.db.user.User;

import javax.servlet.http.HttpSession;

public class SessionUtils {

   
    public static final String SESSION_USER = "user";


    public static void initSession(User user){
        HttpSession session = RequestHolder.getSession();
        session.setAttribute(SESSION_USER,user);
    }

    public static void clearSession(){
        HttpSession session = RequestHolder.getSession();
        session.invalidate();
    }

    public static User getSessionUser(){
        HttpSession session = RequestHolder.getSession();
        User user = (User)session.getAttribute(SESSION_USER);
        return user;
    }


}

4、收集URI


import com.sankuai.windmill.riding.core.api.utils.URIUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.Map;
import java.util.Set;


@Slf4j
@Service
public class UriCollectRunner implements ApplicationRunner {

    @Autowired
    private WebApplicationContext applicationContext;

    /**
     * 获取所有的uri
     */
    @Override
    public void run(ApplicationArguments args) {
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
        for (RequestMappingInfo info : map.keySet()) {
            Set<String> patterns = info.getPatternsCondition().getPatterns();
            for (String uri : patterns) {
                URIUtils.PROJECT_URI.add(uri);
            }
        }
    }
}

ContactAuthor