1. 程式人生 > >使用filter進行登入的攔截,統一驗證使用者登入資訊是否有效

使用filter進行登入的攔截,統一驗證使用者登入資訊是否有效

1需求

在使用者進行所有的操作之前都要驗證當前使用者是否登入有效,如果每個方法呼叫前都去驗證顯得比較蠢,於是使用filter在介面之前統一攔截驗證;

2.方案的選擇

簡單的來說就是實現使用者的登入。剛開始我是採用session和cookie進行使用者登入的,可是涉及到跨域的問題。其實我在網上查了下,前端可以實現跨域的請求帶上cookie。可是人家前端不改!!!!!!!!微笑。非得要用在請求中帶入token,後端自己驗證token是否有效的流程!再次告訴自己微笑。

剛開始我採用的是intercepter,攔截器,但是不行,後文會有介紹。於是採用了filter,過濾器。

3.實現

1.首先在web.xml檔案中配置攔截路徑

  <!-- 使用filter實現登入控制  -->
  <filter>
    <filter-name>SessionFilter</filter-name>
    <filter-class>com.aaa.bbb.controller.login.LoginInterceptor</filter-class>
    <init-param>
    <param-name>ignores</param-name>
    <param-value>/api/web/login,api/web/sso_login,/web/*</param-value>
    </init-param>

  </filter>
  <filter-mapping>
    <filter-name>SessionFilter</filter-name>
    <url-pattern>/api/web/*</url-pattern>
    <!-- <url-pattern>/api/no/*</url-pattern> -->
  </filter-mapping>

<param-name>ignores</param-name>
    <param-value>/api/web/login,api/web/sso_login,/web/*</param-value>

這裡是不進行攔截的路徑,會在com.aaa.bbb.controller.login.LoginInterceptor 中進行過濾

<url-pattern>/api/web/*</url-pattern> 進行攔截的路徑

2.攔截器

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.CrossOrigin;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jf.cloud.config.StaticMapUserInfo;
import com.mysql.fabric.xmlrpc.base.Data;

 

 

/**
 * @Description
 * @Author
 * @Date 2018年11月2日
 */
@CrossOrigin(origins = "*", maxAge = 3600)
public class LoginInterceptor  implements Filter{
    
    
     private String excludedPage;
     private String[] excludedPages;
    
     //sso的訪問地址
     @Value("${ssoURL}")
     String ssoURL;
        

     //定義filter不攔截的路徑
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
         //此處的ignores就是在web.xml定義的名稱一樣。
         excludedPage = filterConfig.getInitParameter("ignores");
        
         if (excludedPage != null && excludedPage.length() > 0){
             excludedPages = excludedPage.split(",");
             }
        
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        
        //解決跨域問題,統一處理
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
       
        
        //由於httprequest的getReader只能使用一次,這裡將reader流儲存下來
        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
        //排除不使用filter的路徑
        for (String page : excludedPages) {
            String url = ((HttpServletRequest) request).getServletPath();
            if (url.equals(page)) {
                chain.doFilter(requestWrapper, resp);
                return;
            }

        }
        
    
        //讀取http request 請求體總body的資料
         BufferedReader br = null;
            StringBuilder sb = new StringBuilder("");
            try
            {
              
                br =  requestWrapper.getReader();
                String str;
                while ((str = br.readLine()) != null)
                {
                    sb.append(str);
                }
                br.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
            finally
            {
                if (null != br)
                {
                    try
                    {
                        br.close();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
          
                    
    
        //判斷是否已經登入            
        String data = sb.toString();
        
        //驗證使用者登入格式
        if(data.equals("")){
            resp.setCharacterEncoding("UTF-8");  
            resp.setContentType("application/json; charset=utf-8");
            PrintWriter out = null ;
            JSONObject res = new JSONObject();
            res.put("result",false);
            res.put("code","400");
            res.put("message","驗證格式未正確輸入");
            out = resp.getWriter();
            out.append(res.toString());
            return ;
        }
        
        JSONObject jvo  = JSONObject.parseObject(data).getJSONObject("vertification");
        
        //拿到使用者登入的token
        String token = jvo.getString("tokenID");
        //type用於區分是手機登入還是電腦登入,走不同的驗證
        String type = jvo.getString("type");
        
        if(type.equals("pc")){//web的驗證
            
            if(StaticMapUserInfo.getUserInfoMap().containsKey(token)){//如果使用者已經登入
                
                //判斷使用者登入資訊是否失效
                Date lastTime = (Date) StaticMapUserInfo.getUserInfoMap().get(token+"_time");
                Date now = new Date();
                if((now.getTime()-lastTime.getTime())<60*2*1000*60){//2小時登入有效
                    StaticMapUserInfo.getUserInfoMap().put(token+"_time", now);
                    chain.doFilter(requestWrapper, resp);
                }else{//使用者登入失效
                    //移除使用者登入資訊
                    StaticMapUserInfo.getUserInfoMap().remove(token);
                    StaticMapUserInfo.getUserInfoMap().remove(token+"_time");
                    
                    //提示使用者登入
                    resp.setCharacterEncoding("UTF-8");  
                    resp.setContentType("application/json; charset=utf-8");
                    PrintWriter out = null ;
                    JSONObject res = new JSONObject();
                    res.put("result",false);
                    res.put("code","414");
                    res.put("message","當前使用者登入已經失效,請重新登入");
                    out = resp.getWriter();
                    out.append(res.toString());
                }
                
                
                
            }else{
                resp.setCharacterEncoding("UTF-8");  
                resp.setContentType("application/json; charset=utf-8");
                PrintWriter out = null ;
                JSONObject res = new JSONObject();
                res.put("result",false);
                res.put("code","400");
                res.put("message","當前使用者未登入,請登入");
                out = resp.getWriter();
                out.append(res.toString());
              
            }
            
            
        }else if(type.equals("app")){//手機的驗證
            
            //封裝sso token驗證的引數
            Map<String, Object> param = new HashMap<String, Object>();
            //拿到使用者登入的token
            Map<String, String> tokenmap = new HashMap<String, String>();
            tokenmap.put("TokenID", token);
            param.put("Method", "aaa.ssouserauth");
            param.put("Version","3.1");
            param.put("Certification", tokenmap);
            
            JSON json = (JSON) JSONObject.toJSON(param);
            
            
            //訪問sso的登入,並返回登陸結果
            JSONObject rso = SSOLoginController.sendPost(ssoURL,json);
            if (rso.getBooleanValue("Result")) {//使用者登入成功
                chain.doFilter(requestWrapper, resp);
            }else{
                resp.setCharacterEncoding("UTF-8");  
                resp.setContentType("application/json; charset=utf-8");
                PrintWriter out = null ;
                JSONObject res = new JSONObject();
                res.put("result",false);
                res.put("code","400");
                res.put("message",rso.getString("Message"));
                out = resp.getWriter();
                out.append(res.toString());
            }
            
            
            
            
        }else{
            resp.setCharacterEncoding("UTF-8");  
            resp.setContentType("application/json; charset=utf-8");
            PrintWriter out = null ;
            JSONObject res = new JSONObject();
            res.put("result",false);
            res.put("code","400");
            res.put("message","驗證型別錯誤");
            out = resp.getWriter();
            out.append(res.toString());
          
        }
        
        

        
        
    
        

}
        
    

    @Override
    public void destroy() {
        // TODO Auto-generated method stub
        
    }
    
}


    

 

 

 //sso的訪問地址
     @Value("${ssoURL}")
     String ssoURL;

其他系統的sso驗證地址

 

//解決跨域問題,統一處理
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        resp.setHeader("Access-Control-Max-Age", "3600");
        resp.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");

這裡對response最跨域的處理,必須加上

/**
 * @Description
 * @Author
 * @Date 2018年11月5日
 */

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class BodyReaderHttpServletRequestWrapper
        extends
            HttpServletRequestWrapper {
    

    private final byte[] body;

    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request)
            throws IOException {
        
        super(request);
        body = toByteArray(request.getInputStream());
    }

    private byte[] toByteArray(InputStream in) throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int n = 0;
        while ((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }
}

 

//由於httprequest的getReader只能使用一次,這裡將reader流儲存下來
        BodyReaderHttpServletRequestWrapper requestWrapper = new BodyReaderHttpServletRequestWrapper((HttpServletRequest) request);
   否則會報錯   

getReader() has already been called for this request

 

 

import java.util.HashMap;
import java.util.Map;

 

/**
 * @Description 儲存使用者登入資訊
 * @Author zengzhiqiang
 * @Date 2018年11月5日
 */

public class StaticMapUserInfo {
    
    public static  Map<String, Object> userInfoMap ;
    
    
    
    
    public static Map<String, Object> getUserInfoMap() {
        
        if(userInfoMap==null){
            userInfoMap = new HashMap<String, Object>();
        }
        return userInfoMap;
    }
    
    
    public static void setUserInfoMap(Map<String, Object> userInfoMap) {
        StaticMapUserInfo.userInfoMap = userInfoMap;
    }

    
}

 

這裡使用了一個單列的map代替redis的儲存作用

 

 /**
     * 向指定的 URL傳送遠端POST方法的請求
     *
     * @param url傳送請求的  URL
     * @param json請求引數,
     * @return 所代表遠端資源的響應結果
     */
    public static JSONObject sendPost(String url, JSON json) {
        PrintWriter out = null;
        BufferedReader in = null;
        JSONObject jsonObject = null;
        String result = "";
        try {
            URL realUrl = new URL(url);
            // 開啟和URL之間的連線
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            // 設定通用的請求屬性
            conn.setRequestMethod("POST");
            // 傳送POST請求必須設定下面的屬性
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setUseCaches(false);
            //設定請求屬性
            conn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
            conn.connect();
            // 獲取URLConnection物件對應的輸出流
            out = new PrintWriter(conn.getOutputStream());
            // 傳送請求引數
            out.print(JSON.toJSONString(json));
            // flush輸出流的緩衝
            out.flush();
            // 定義BufferedReader輸入流來讀取URL的響應
            in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line = "";
            while ((line = in.readLine()) != null) {
                result += line;
            }
            //將返回結果轉換為字串
            jsonObject = JSONObject.parseObject(result);
        } catch (Exception e) {
            throw new RuntimeException("遠端通路異常" + e.toString());
        }
        // 使用finally塊來關閉輸出流、輸入流
        finally {
            try {
                if (out != null) {
                    out.close();
                }
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return jsonObject;
    }

 

驗證其他系統的token在本系統的使用

3.入參

 如果使用interceper,程式執行不會報錯,但是會出現錯誤

Failed to read HTTP message Required request body is missing:

會解析不到處理後的request body,這可能會與標籤 @requestbody,有關

 

結束!

=======================================

看雲,看雨,看天,看未知的秋天 ----記錄