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,有关
结束!
=======================================
看云,看雨,看天,看未知的秋天 ----记录