单点登陆性能测试方案

项目登陆系统升级,改成单点登陆:英文全称Single Sign On。SSO是在多个应用系统中,用户只须要登陆一次就能够访问全部相互信任的应用系统。 以前有的统一登陆方式被废弃,因为单点登陆比较以前的登陆系统复杂不少。以前的方案请求一个接口便可得到用户校验令牌。 先分享一下单点登陆的技术方案的时序图:前端

单点登陆时序图

而后发一下我梳理的前端调用接口的时序图:java

单点登陆时序图

性能测试分红了两个场景: 性能压测场景分析: 跳过没必要要的302响应状态请求,只测试业务逻辑相关接口,不处理页面相关接口(资源文件等),登陆完成请求额外接口完成登陆验证。python

  • 场景一:单个用户登陆单个系统。 第一步:请求cas服务login页面,解析页面获取秘钥串(lt/execution) 第二步:请求cas服务登陆接口,获取TGC令牌和ST令牌 第三步:请求svr服务校验ST令牌,获取admin_jsessionid信息 第四步:请求额外接口完成登陆状态验证web

  • 场景二:单个用户登陆两个系统 第一步:请求cas服务login页面,解析页面获取秘钥串(lt/execution) 第二步:请求cas服务登陆接口,获取TGC令牌和ST1令牌 第三步:请求svr1服务校验ST1令牌,获取admin_jsessionid信息 第四步:请求额外接口完成登svr1录状态验证 第五步:请求cas服务登陆接口(携带TGC令牌),获取svr2对应的ST2令牌 第六步:请求svr2服务校验校验ST2令牌,获取admin_jsessionid信息 第七步:请求额外接口完成svr2登陆状态校验apache

针对这两个场景,测试脚本以下:编程

import com.fun.base.constaint.ThreadBase
import com.fun.config.SqlConstant
import com.fun.frame.excute.Concurrent
import com.fun.utils.Time
import com.okayqa.teacherweb.base.OkayBase
import org.slf4j.Logger
import org.slf4j.LoggerFactory

class Tss extends OkayBase {
    private static Logger logger = LoggerFactory.getLogger(Tss.class)


    public static void main(String[] args) {
        def threadNum = changeStringToInt(args[0])
        def times = changeStringToInt(args[1])
        SqlConstant.flag = false


//        def threadNum = 3
//        def times = 2
        def arrayList = new ArrayList<ThreadBase>()
        for (int i = 0; i < threadNum; i++) {
            def thread = new ThreadBase<Integer>(new Integer(i)) {
                @Override
                protected void before() {

                }

                @Override
                protected void doing() throws Exception {
                    def mark = Time.getTimeStamp()
                    def base = getBase(changeStringToInt(getT()))
//                    def cookies = base.getCookies()
//                    def base1 = new com.okayqa.publicweb.base.OkayBase() //建立public-web项目的用户对象
//                    base1.init(cookies)//初始化用户对象
//                    def common = new SchoolCommon(base1)//建立学校公共接口请求对象
//                    def years = common.getYears()//请求学校学年接口
                    def mark0 = Time.getTimeStamp()
                    def i1 = mark0 - mark
                    logger.error("----------------" + i1 + EMPTY)
                }

                @Override
                protected void after() {

                }
            }

            thread.setTimes(times)
            arrayList << thread
        }
        new Concurrent(arrayList).start()
//
        allOver()

    }
}

首先各个项目用户对象代码以下:json

package com.okayqa.teacherweb.base;

import com.fun.base.bean.BeanUtil;
import com.fun.base.bean.RequestInfo;
import com.fun.base.interfaces.IBase;
import com.fun.config.HttpClientConstant;
import com.fun.config.SqlConstant;
import com.fun.config.SysInit;
import com.fun.frame.SourceCode;
import com.fun.frame.httpclient.FanLibrary;
import com.okayqa.common.CasCredential;
import com.okayqa.common.Common;
import com.okayqa.common.Users;
import com.okayqa.teacherweb.bean.UserInfoBean;
import com.okayqa.teacherweb.function.UserCenter;
import com.okayqa.teacherweb.profile.Profile;
import com.okayqa.teacherweb.profile.UserApi;
import net.sf.json.JSONObject;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
 * 教师空间项目
 * qa项目base类
 */
public class OkayBase extends SourceCode implements IBase {

    private static Logger logger = LoggerFactory.getLogger(OkayBase.class);

    private static OkayBase base;

    static {
        SqlConstant.REQUEST_TABLE = Common.SQL_REQUEST;
        SqlConstant.flag = Common.SQL_KEY;
        SqlConstant.PERFORMANCE_TABLE = Common.SQL_PERFORMANCE;
        if (FanLibrary.getiBase() == null) FanLibrary.setiBase(new OkayBase());
    }

    public final static String HOST = Profile.HOST;


    /**
     * 登陆响应
     */
    JSONObject loginResponse;

    private UserInfoBean userInfoBean = new UserInfoBean();

    /**
     * 获取对象方法
     * <p>
     * 暂未进行用户管理,赞成使用单例
     * </p>
     *
     * @return
     */
    public static OkayBase getBase() {
        if (base == null) base = new OkayBase(0);
        return base;
    }

    public static OkayBase getBase(int i) {
        return new OkayBase(i);
    }

    public static OkayBase getBase(String name) {
        return new OkayBase(name);
    }

    long uid;

    String token;

    String username;

    public JSONObject getCookies() {
        return cookies;
    }

    public void setCookies(JSONObject cookies) {
        this.cookies = cookies;
    }

    public void addCookie(JSONObject cookies) {
        this.cookies.putAll(cookies);
    }

    JSONObject cookies = new JSONObject();


    @Override
    public void login() {
//        /**
//         * 单点登陆方式
        String url = UserApi.LOGIN;
        JSONObject params = new JSONObject();
        params.put("loginType", "1");
        params.put("platformType", "teacher");
        params.put("username", username);
        params.put("password", getPassword());
        params.put("pictureVerifyCode", "");
        params.put("phone", "");
        params.put("traceno", "");
        params.put("phoneVerifyCode", "");
        JSONObject tgc = CasCredential.getTGC(HOST, params);
        this.cookies = tgc.getJSONObject("cookie");
        String location = tgc.containsKey("location") ? tgc.getString("location") : EMPTY;
        if (!location.contains("ticket=ST-")) logger.error("登陆失败!");
        JSONObject getResponse = this.getGetResponse(location.replace(HOST, EMPTY));
        UserCenter userCenter = new UserCenter(this.cookies);
        userInfoBean = userCenter.getUserinfo();
        logger.info("帐号:{},昵称:{},学科名称:{},登陆成功!", username,userInfoBean.getName(),userInfoBean.getSubjectName());
    }

    /**
     * 获取到明文的默认密码
     *
     * @return
     */
    public String getPassword() {
        return Profile.PWD;
    }

    public OkayBase(String username) {
        this.username = username;
        login();
    }

    public OkayBase(int i) {
        this.username = Users.getTeaUser(i);
        login();
    }

    protected OkayBase() {
    }

    public OkayBase(OkayBase okayBase) {
        this.uid = okayBase.uid;
        this.username = okayBase.username;
        this.token = okayBase.token;
        this.userInfoBean = okayBase.userInfoBean;
        this.cookies = okayBase.cookies;
    }

    public JSONObject getParams() {
        return getJson("_=" + getMark());
    }


    @Override
    public void init(JSONObject jsonObject) {
        addCookie(jsonObject);
        HttpGet get = FanLibrary.getHttpGet(Profile.LOGIN_REDIRECT);
        get.addHeader(FanLibrary.getCookies(jsonObject));
        JSONObject response = FanLibrary.getHttpResponse(get);
        JSONObject credential = CasCredential.verifyST(response.getString("location"));
        addCookie(credential);
    }

    public JSONObject getLoginResponse() {
        return loginResponse;
    }

    public long getUid() {
        return uid;
    }

    public String getToken() {
        return token;
    }

    public String getUname() {
        return username;
    }

    public UserInfoBean getUserInfoBean() {
        return userInfoBean;
    }

    @Override
    public HttpGet getGet(String s) {
        return FanLibrary.getHttpGet(HOST + s);
    }

    @Override
    public HttpGet getGet(String s, JSONObject jsonObject) {
        return FanLibrary.getHttpGet(HOST + s, jsonObject);
    }

    @Override
    public HttpPost getPost(String s) {
        return FanLibrary.getHttpPost(HOST + s);
    }

    @Override
    public HttpPost getPost(String s, JSONObject jsonObject) {
        return FanLibrary.getHttpPost(HOST + s, jsonObject);
    }

    @Override
    public HttpPost getPost(String s, JSONObject jsonObject, File file) {
        return FanLibrary.getHttpPost(HOST + s, jsonObject, file);
    }

    @Override
    public JSONObject getResponse(HttpRequestBase httpRequestBase) {
        setHeaders(httpRequestBase);
        JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
        handleResponseHeader(response);
        return response;
    }

    @Override
    public void setHeaders(HttpRequestBase httpRequestBase) {
        httpRequestBase.addHeader(Common.REQUEST_ID);
        this.addCookie(getJson("user_phone_check_" + this.username + "=true"));
        if (!cookies.isEmpty()) httpRequestBase.addHeader(FanLibrary.getCookies(cookies));
    }

    @Override
    public void handleResponseHeader(JSONObject response) {
        if (!response.containsKey(HttpClientConstant.COOKIE)) return;
        cookies.putAll(response.getJSONObject(HttpClientConstant.COOKIE));
        response.remove(HttpClientConstant.COOKIE);
    }

    @Override
    public JSONObject getGetResponse(String s) {
        return getResponse(getGet(s));
    }

    @Override
    public JSONObject getGetResponse(String s, JSONObject jsonObject) {
        return getResponse(getGet(s, jsonObject));
    }

    @Override
    public JSONObject getPostResponse(String s) {
        return getResponse(getPost(s));
    }

    @Override
    public JSONObject getPostResponse(String s, JSONObject jsonObject) {
        return getResponse(getPost(s, jsonObject));
    }

    @Override
    public JSONObject getPostResponse(String s, JSONObject jsonObject, File file) {
        return getResponse(getPost(s, jsonObject, file));
    }

    @Override
    public boolean isRight(JSONObject jsonObject) {
        if (jsonObject.containsKey("success")) return jsonObject.getBoolean("success");
        int code = checkCode(jsonObject, new RequestInfo(getGet(HOST)));
        try {
            JSONObject data = jsonObject.getJSONObject("data");
            return code == 0 && !data.isEmpty();
        } catch (Exception e) {
            output(jsonObject);
            return false;
        }

    }

    /**
     * 获取并检查code
     *
     * @param jsonObject
     * @return
     */
    public int checkCode(JSONObject jsonObject, RequestInfo requestInfo) {
        int code = TEST_ERROR_CODE;
        if (SysInit.isBlack(requestInfo.getHost())) return code;
        try {
            code = jsonObject.getInt("code");
        } catch (Exception e) {
            logger.warn("非标准响应:{}", jsonObject.toString());
        }
        return code;
    }

    /**
     * 测试结束,资源释放
     */
    public static void allOver() {
        FanLibrary.testOver();
    }

}

统一验证类的代码以下:cookie

package com.okayqa.common

import com.fun.config.HttpClientConstant
import com.fun.frame.httpclient.FanLibrary
import com.fun.utils.Regex
import net.sf.json.JSONObject
import org.apache.http.client.methods.HttpGet
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
 * cas服务验证类,主要解决web端登陆验证功能
 */
class CasCredential extends FanLibrary {
    static final String OR="/"
    private static Logger logger = LoggerFactory.getLogger(CasCredential.class)
    /**
     * 校验值,随机一次性,从login返回页面中获取
     */
    String lt
    /**
     * 校验值,随机一次性,从login返回页面中获取,正常值长度在4000+,低于4000请检查请求链接是否传入了回调服务的地址
     */
    String execution

/**
 * 从cas服务的login页面获取到令牌对,此处正则暂时可用,二期会修改表单提交
 */
    CasCredential(String host) {
        def get = getHttpGet(Common.CAS_LOGIN + (host.endsWith(OR) ? host : host + OR))
        get.addHeader(Common.REQUEST_ID)
        def response = getHttpResponse(get)
        def string = response.getString("content")
        this.lt = Regex.getRegex(string, "<input type=\"hidden\" name=\"lt\" value=\".*?\" />")
        this.execution = Regex.getRegex(string, " <input type=\"hidden\" name=\"execution\" value=\".*?\" />")
//        logger.info("cas服务登陆host:{},lt:{},execution:{}", host, lt, execution)
    }

/**
 * 各个服务端参数一致,由各个服务本身把参数拼好以后传过来,以后在去cas服务拿到令牌对
 * @param host 服务的host地址,回调由各个服务本身完成,二次验证也是,此处的host不作兼容,有cascredential作处理
 * @param params 拼好的参数
 * @return
 */
    static JSONObject getTGC(String host, JSONObject params) {
        def credential = new CasCredential(host)
        params.put("lt", credential.getLt());
        params.put("execution", credential.getExecution())
        params.put("_eventId", "submit");
        def post = FanLibrary.getHttpPost(Common.CAS_LOGIN + (host.endsWith(OR) ? host : host + OR), params)
        post.addHeader(Common.REQUEST_ID);
        FanLibrary.getHttpResponse(post)
    }

/**
 * 经过用户
 * @param url
 * @return
 */
    public static JSONObject verifyST(String url) {
        HttpGet location = FanLibrary.getHttpGet(url);
        location.addHeader(Common.REQUEST_ID);
        JSONObject httpResponse = FanLibrary.getHttpResponse(location);
        httpResponse.getJSONObject(HttpClientConstant.COOKIE) as JSONObject
    }
}

而后顺利完工。由于以前性能测试方案都是使用jmeter做为解决方案,此次架构变动的测试用例难以实现,故才用了脚本。性能框架才用了以前发过的性能测试框架有兴趣的能够点击查看一下,语言以Java为主,脚本使用Groovy写的。session

技术类文章精选

非技术文章精选

大咖风采

点击查看公众号地图

相关文章
相关标签/搜索