您的位置:首页 > 运维架构 > 网站架构

记一次通过程序自动登录需SSO认证的网站

2017-08-02 22:03 302 查看

背景

一个朋友,做车险的,业务员。每天系统会产生大量的险单,而这些数据,他需要手动录入到EXCEL中,留作它用。时间久了,感觉手动处理太过于麻烦,请我帮忙能不能写一个程序,自动把数据按要求写入EXCEL中。

思路

之前做过网页爬取、数据提取等案例,感觉问题不大,用到的工具就是firebug、httpclient、jsoup、poi。

分析firebug抓取到的HTTP相关数据包,找出业务相关的URL、需要的参数、请求头、以及响应数据格式。然后使用httpclient模拟客户端与服务端进行交互,对响应结果进行解析(如果响应的数据是html,使用jsoup可以很方便的获取想要的数据,如果是json,就更简单了,直接使用第三方的json库,转化成数据封装对象),最后将数据整理、使用第三方库poi按要求将数据写入EXCEL中。

问题

本来想的是直接请求业务URL,处理即可。没想到的是,这个URL访问需要认证登录才可以,直接访问的话,会被拒绝,拿到401响应码。看来,只能分析这个网站的认证登录流程,然后使用httpclient模拟完成认证。

动手

当时没有详细去了解下CAS认证协议,仅仅是通过数据包分析去完成认证登录的业务流程。所以下面的代码也是过程式的,没有去重构。

package com.ztbx.auth;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import com.ztbx.util.SimpleHttpClient;

/**
* 完成cas认证
*
* @author cjw
*/
public class CasLogin {

protected static final Log log = LogFactory.getLog(CasLogin.class);

static final String FIRST_URL = "http://xxx.xxx.xxx.xxx:xxxx/prpcar/main/index";
static final String CAS = "http://xx.xx.xx.xx:xx/casserver/login;jsessionid=";

public static void login() throws Exception {
//先去请求一下目标地址
HttpResponse firstResp = SimpleHttpClient.requestPost(FIRST_URL, null);
log.info("step1状态码:" + firstResp.getStatusLine().getStatusCode());
String authURL = "";
if (firstResp.getStatusLine().getStatusCode() == 302) { //需要认证
Header[] hs = firstResp.getHeaders("Location");
authURL = hs[0].getValue();
}
SimpleHttpClient.printResponse(firstResp);

//访问认证URL
HttpResponse secondResp = SimpleHttpClient.requestPost(authURL, null);
log.info("step2状态码:" + secondResp.getStatusLine().getStatusCode());
String respHtml = SimpleHttpClient.printResponse(secondResp);

//解析隐藏域的参数
String ltparam = null;
String executionparam = null;
if (respHtml != null) {
String ltkey = "<input type=\"hidden\" name=\"lt\" value=\"";
int ltbi = respHtml.indexOf(ltkey);
String temp = respHtml.substring(ltbi + ltkey.length());
int ltei = temp.indexOf("\" />");
ltparam = temp.substring(0, ltei);
log.info("ltparam:" + ltparam);

String executionkey = "<input type=\"hidden\" name=\"execution\" value=\"";
int executionbi = temp.indexOf(executionkey);
temp = temp.substring(executionbi + executionkey.length());
int executionei = temp.indexOf("\" />");
executionparam = temp.substring(0, executionei);
log.info("executionparam:" + executionparam);

}

//需要获取JSESSIONID
String scStr = SimpleHttpClient.getCookies(secondResp);
String jsessionid = scStr.substring("JSESSIONID=".length(), scStr.indexOf(";"));
String casURL = CAS + jsessionid + authURL.substring(authURL.indexOf("?"));
//http://xx.xx.xx.xx:xx/casserver/login;jsessionid=pYn3WqdFTqQvCSZQnK1SQtGyBv0RxWRg84R0RhNQCzzkn6Kx6ymN!811231245?service=http%3A%2F%2Fxx.xx.xx.xx%3Axxxx%2Fprpcar%2Fj_spring_cas_security_check%3BJSESSIONIDPRPCAR69%3DMWWhWqcZ0mgBh5kyKKTLcLT84wrFznWy7pff7yTJ7fTzYDH02nHN%21-1319107732&renew=true

//读取配置文件,获取用户登录信息
PropertiesConfiguration config = new PropertiesConfiguration("ztbx.properties");

//模拟登录
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("lt", ltparam));
params.add(new BasicNameValuePair("execution", executionparam));
params.add(new BasicNameValuePair("_eventId", "submit"));
params.add(new BasicNameValuePair("username", config.getString("username")));
params.add(new BasicNameValuePair("password", config.getString("password")));
HttpResponse response = SimpleHttpClient.requestPost(casURL, params);
log.info("step3状态码:" + response.getStatusLine().getStatusCode());
SimpleHttpClient.printResponse(response);

String ticketCheckURL = "";
if (response.getStatusLine().getStatusCode() == 302) { //重定向
Header[] hs = response.getHeaders("Location");
ticketCheckURL = hs[0].getValue();
}
HttpResponse fourResp = SimpleHttpClient.requestPost(ticketCheckURL, null);
log.info("step4状态码:" + fourResp.getStatusLine().getStatusCode());
SimpleHttpClient.printResponse(fourResp);

String destURL = "";
if (fourResp.getStatusLine().getStatusCode() == 302) {
Header[] hs = fourResp.getHeaders("Location");
destURL = hs[0].getValue();
}
HttpResponse fiveResp = SimpleHttpClient.requestPost(destURL, null);
log.info("step5状态码:" + fiveResp.getStatusLine().getStatusCode());
SimpleHttpClient.printResponse(fiveResp);

//验证
String validMacURL = "";
if (fiveResp.getStatusLine().getStatusCode() == 302) {
Header[] hs = fiveResp.getHeaders("Location");
validMacURL = hs[0].getValue();
}
HttpResponse sixResp = SimpleHttpClient.requestPost(validMacURL, null);
log.info("step6状态码:" + sixResp.getStatusLine().getStatusCode());
SimpleHttpClient.printResponse(sixResp);

//访问目标首页
String destIndex = "";
if (sixResp.getStatusLine().getStatusCode() == 302) {
Header[] hs = sixResp.getHeaders("Location");
destIndex = hs[0].getValue();
}
HttpResponse resp = SimpleHttpClient.requestPost(destIndex, null);
log.info("最终状态码:" + resp.getStatusLine().getStatusCode());
SimpleHttpClient.printResponse(resp);
}

}

HTTP客户端工具类:

这个类注意下它的类成员变量,这个客户端是使用了一个长连接,为的就是能像浏览器一样,把这次会话给保持住,避免后续模拟的业务查询被认为是一个新的连接,而重新认证(当然,也可以每次使用的时候重新创建一个连接,然后把cookie传进去,可以达到同样的效果)。

package com.ztbx.util;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.util.EntityUtils;

/**
* HTTP客户端
*
* @author cjw
*/
public class SimpleHttpClient {

protected static final Log log = LogFactory.getLog(SimpleHttpClient.class);

//创建CookieStore实例
static CookieStore cookieStore = null;
static HttpClient httpclient = null;
static Header[] headers = null;

static {
cookieStore = new BasicCookieStore();
httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setDefaultHeaders(defaultHeader())
.build();
}

private static List<Header> defaultHeader() {
ArrayList<Header> headers = new ArrayList<Header>();
headers.add(new BasicHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0"));
headers.add(new BasicHeader(HttpHeaders.ACCEPT, "application/json, text/javascript, */*; q=0.01"));
headers.add(new BasicHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3"));
headers.add(new BasicHeader(HttpHeaders.ACCEPT_CHARSET, "GBK,utf-8;q=0.7,*;q=0.7"));
return headers;
}

public static HttpResponse requestPost(String url, List<NameValuePair> params) throws Exception {
HttpPost httppost = new HttpPost(url);
httppost.setHeaders(headers);
if (params!= null && !params.isEmpty()) {
httppost.setEntity(new UrlEncodedFormEntity(params));
}
HttpResponse response = httpclient.execute(httppost);
return response;
}

public static String getCookies(HttpResponse response) {
Header[] headers = response.getHeaders("Set-Cookie");
StringBuilder sb = new StringBuilder();
for (Header header : headers) {
sb.append(header.getValue()).append(";");
}
return sb.toString();
}

public static void printAllHeader(HttpResponse response) {
Header[] headers = response.getAllHeaders();
for (Header header : headers) {
log.info(header.getName() + " " + header.getValue());
}
}

public static String printResponse(HttpResponse httpResponse)
throws ParseException, IOException {
// 获取响应消息实体
HttpEntity entity = httpResponse.getEntity();
// 响应状态
log.info("status:" + httpResponse.getStatusLine());
log.info("headers:");
HeaderIterator iterator = httpResponse.headerIterator();
while (iterator.hasNext()) {
log.info("\t" + iterator.next());
}
// 判断响应实体是否为空
String responseString = null;
if (entity != null) {
responseString = EntityUtils.toString(entity, "UTF-8");
log.info("response length:" + responseString.length());
log.info("response content:"
+ responseString.replace("\r\n", ""));
}
return responseString;
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息