使用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,有關
結束!
=======================================
看雲,看雨,看天,看未知的秋天 ----記錄