ITPub博客

首页 > 数据库 > 数据库开发技术 > 单点登录性能测试方案

单点登录性能测试方案

原创 数据库开发技术 作者:FunTester 时间:2019-10-08 09:46:44 0 删除 编辑


项目登录系统升级,改为单点登录:英文全称Single Sign On。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
之前有的统一登录方式被废弃,由于单点登录比较之前的登录系统复杂很多。之前的方案请求一个接口即可获得用户校验令牌。
先分享一下单点登录的技术方案的时序图:

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

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

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

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

针对这两个场景,测试脚本如下:

 1import com.fun.base.constaint.ThreadBase
2import com.fun.config.SqlConstant
3import com.fun.frame.excute.Concurrent
4import com.fun.utils.Time
5import com.okayqa.teacherweb.base.OkayBase
6import org.slf4j.Logger
7import org.slf4j.LoggerFactory
8
9class Tss extends OkayBase {
10    private static Logger logger = LoggerFactory.getLogger(Tss.class)
11
12
13    public static void main(String[] args) {
14        def threadNum = changeStringToInt(args[0])
15        def times = changeStringToInt(args[1])
16        SqlConstant.flag = false
17
18
19//        def threadNum = 3
20//        def times = 2
21        def arrayList = new ArrayList<ThreadBase>()
22        for (int i = 0; i < threadNum; i++) {
23            def thread = new ThreadBase<Integer>(new Integer(i)) {
24                @Override
25                protected void before() {
26
27                }
28
29                @Override
30                protected void doing() throws Exception {
31                    def mark = Time.getTimeStamp()
32                    def base = getBase(changeStringToInt(getT()))
33//                    def cookies = base.getCookies()
34//                    def base1 = new com.okayqa.publicweb.base.OkayBase() //创建public-web项目的用户对象
35//                    base1.init(cookies)//初始化用户对象
36//                    def common = new SchoolCommon(base1)//创建学校公共接口请求对象
37//                    def years = common.getYears()//请求学校学年接口
38                    def mark0 = Time.getTimeStamp()
39                    def i1 = mark0 - mark
40                    logger.error("----------------" + i1 + EMPTY)
41                }
42
43                @Override
44                protected void after() {
45
46                }
47            }
48
49            thread.setTimes(times)
50            arrayList << thread
51        }
52        new Concurrent(arrayList).start()
53//
54        allOver()
55
56    }
57}

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

  1package com.okayqa.teacherweb.base;
2
3import com.fun.base.bean.BeanUtil;
4import com.fun.base.bean.RequestInfo;
5import com.fun.base.interfaces.IBase;
6import com.fun.config.HttpClientConstant;
7import com.fun.config.SqlConstant;
8import com.fun.config.SysInit;
9import com.fun.frame.SourceCode;
10import com.fun.frame.httpclient.FanLibrary;
11import com.okayqa.common.CasCredential;
12import com.okayqa.common.Common;
13import com.okayqa.common.Users;
14import com.okayqa.teacherweb.bean.UserInfoBean;
15import com.okayqa.teacherweb.function.UserCenter;
16import com.okayqa.teacherweb.profile.Profile;
17import com.okayqa.teacherweb.profile.UserApi;
18import net.sf.json.JSONObject;
19import org.apache.http.client.methods.HttpGet;
20import org.apache.http.client.methods.HttpPost;
21import org.apache.http.client.methods.HttpRequestBase;
22import org.slf4j.Logger;
23import org.slf4j.LoggerFactory;
24
25import java.io.File;
26
27/**
28 * 教师空间项目
29 * qa项目base类
30 */

31public class OkayBase extends SourceCode implements IBase {
32
33    private static Logger logger = LoggerFactory.getLogger(OkayBase.class);
34
35    private static OkayBase base;
36
37    static {
38        SqlConstant.REQUEST_TABLE = Common.SQL_REQUEST;
39        SqlConstant.flag = Common.SQL_KEY;
40        SqlConstant.PERFORMANCE_TABLE = Common.SQL_PERFORMANCE;
41        if (FanLibrary.getiBase() == null) FanLibrary.setiBase(new OkayBase());
42    }
43
44    public final static String HOST = Profile.HOST;
45
46
47    /**
48     * 登录响应
49     */

50    JSONObject loginResponse;
51
52    private UserInfoBean userInfoBean = new UserInfoBean();
53
54    /**
55     * 获取对象方法
56     * <p>
57     * 暂未进行用户管理,同意使用单例
58     * </p>
59     *
60     * @return
61     */

62    public static OkayBase getBase() {
63        if (base == null) base = new OkayBase(0);
64        return base;
65    }
66
67    public static OkayBase getBase(int i) {
68        return new OkayBase(i);
69    }
70
71    public static OkayBase getBase(String name) {
72        return new OkayBase(name);
73    }
74
75    long uid;
76
77    String token;
78
79    String username;
80
81    public JSONObject getCookies() {
82        return cookies;
83    }
84
85    public void setCookies(JSONObject cookies) {
86        this.cookies = cookies;
87    }
88
89    public void addCookie(JSONObject cookies) {
90        this.cookies.putAll(cookies);
91    }
92
93    JSONObject cookies = new JSONObject();
94
95
96    @Override
97    public void login() {
98//        /**
99//         * 单点登录方式
100        String url = UserApi.LOGIN;
101        JSONObject params = new JSONObject();
102        params.put("loginType", "1");
103        params.put("platformType", "teacher");
104        params.put("username", username);
105        params.put("password", getPassword());
106        params.put("pictureVerifyCode", "");
107        params.put("phone", "");
108        params.put("traceno", "");
109        params.put("phoneVerifyCode", "");
110        JSONObject tgc = CasCredential.getTGC(HOST, params);
111        this.cookies = tgc.getJSONObject("cookie");
112        String location = tgc.containsKey("location") ? tgc.getString("location") : EMPTY;
113        if (!location.contains("ticket=ST-")) logger.error("登录失败!");
114        JSONObject getResponse = this.getGetResponse(location.replace(HOST, EMPTY));
115        UserCenter userCenter = new UserCenter(this.cookies);
116        userInfoBean = userCenter.getUserinfo();
117        logger.info("账号:{},昵称:{},学科名称:{},登录成功!", username,userInfoBean.getName(),userInfoBean.getSubjectName());
118    }
119
120    /**
121     * 获取到明文的默认密码
122     *
123     * @return
124     */

125    public String getPassword() {
126        return Profile.PWD;
127    }
128
129    public OkayBase(String username) {
130        this.username = username;
131        login();
132    }
133
134    public OkayBase(int i) {
135        this.username = Users.getTeaUser(i);
136        login();
137    }
138
139    protected OkayBase() {
140    }
141
142    public OkayBase(OkayBase okayBase) {
143        this.uid = okayBase.uid;
144        this.username = okayBase.username;
145        this.token = okayBase.token;
146        this.userInfoBean = okayBase.userInfoBean;
147        this.cookies = okayBase.cookies;
148    }
149
150    public JSONObject getParams() {
151        return getJson("_=" + getMark());
152    }
153
154
155    @Override
156    public void init(JSONObject jsonObject) {
157        addCookie(jsonObject);
158        HttpGet get = FanLibrary.getHttpGet(Profile.LOGIN_REDIRECT);
159        get.addHeader(FanLibrary.getCookies(jsonObject));
160        JSONObject response = FanLibrary.getHttpResponse(get);
161        JSONObject credential = CasCredential.verifyST(response.getString("location"));
162        addCookie(credential);
163    }
164
165    public JSONObject getLoginResponse() {
166        return loginResponse;
167    }
168
169    public long getUid() {
170        return uid;
171    }
172
173    public String getToken() {
174        return token;
175    }
176
177    public String getUname() {
178        return username;
179    }
180
181    public UserInfoBean getUserInfoBean() {
182        return userInfoBean;
183    }
184
185    @Override
186    public HttpGet getGet(String s) {
187        return FanLibrary.getHttpGet(HOST + s);
188    }
189
190    @Override
191    public HttpGet getGet(String s, JSONObject jsonObject) {
192        return FanLibrary.getHttpGet(HOST + s, jsonObject);
193    }
194
195    @Override
196    public HttpPost getPost(String s) {
197        return FanLibrary.getHttpPost(HOST + s);
198    }
199
200    @Override
201    public HttpPost getPost(String s, JSONObject jsonObject) {
202        return FanLibrary.getHttpPost(HOST + s, jsonObject);
203    }
204
205    @Override
206    public HttpPost getPost(String s, JSONObject jsonObject, File file) {
207        return FanLibrary.getHttpPost(HOST + s, jsonObject, file);
208    }
209
210    @Override
211    public JSONObject getResponse(HttpRequestBase httpRequestBase) {
212        setHeaders(httpRequestBase);
213        JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
214        handleResponseHeader(response);
215        return response;
216    }
217
218    @Override
219    public void setHeaders(HttpRequestBase httpRequestBase) {
220        httpRequestBase.addHeader(Common.REQUEST_ID);
221        this.addCookie(getJson("user_phone_check_" + this.username + "=true"));
222        if (!cookies.isEmpty()) httpRequestBase.addHeader(FanLibrary.getCookies(cookies));
223    }
224
225    @Override
226    public void handleResponseHeader(JSONObject response) {
227        if (!response.containsKey(HttpClientConstant.COOKIE)) return;
228        cookies.putAll(response.getJSONObject(HttpClientConstant.COOKIE));
229        response.remove(HttpClientConstant.COOKIE);
230    }
231
232    @Override
233    public JSONObject getGetResponse(String s) {
234        return getResponse(getGet(s));
235    }
236
237    @Override
238    public JSONObject getGetResponse(String s, JSONObject jsonObject) {
239        return getResponse(getGet(s, jsonObject));
240    }
241
242    @Override
243    public JSONObject getPostResponse(String s) {
244        return getResponse(getPost(s));
245    }
246
247    @Override
248    public JSONObject getPostResponse(String s, JSONObject jsonObject) {
249        return getResponse(getPost(s, jsonObject));
250    }
251
252    @Override
253    public JSONObject getPostResponse(String s, JSONObject jsonObject, File file) {
254        return getResponse(getPost(s, jsonObject, file));
255    }
256
257    @Override
258    public boolean isRight(JSONObject jsonObject) {
259        if (jsonObject.containsKey("success")) return jsonObject.getBoolean("success");
260        int code = checkCode(jsonObject, new RequestInfo(getGet(HOST)));
261        try {
262            JSONObject data = jsonObject.getJSONObject("data");
263            return code == 0 && !data.isEmpty();
264        } catch (Exception e) {
265            output(jsonObject);
266            return false;
267        }
268
269    }
270
271    /**
272     * 获取并检查code
273     *
274     * @param jsonObject
275     * @return
276     */

277    public int checkCode(JSONObject jsonObject, RequestInfo requestInfo) {
278        int code = TEST_ERROR_CODE;
279        if (SysInit.isBlack(requestInfo.getHost())) return code;
280        try {
281            code = jsonObject.getInt("code");
282        } catch (Exception e) {
283            logger.warn("非标准响应:{}", jsonObject.toString());
284        }
285        return code;
286    }
287
288    /**
289     * 测试结束,资源释放
290     */

291    public static void allOver() {
292        FanLibrary.testOver();
293    }
294
295}

统一验证类的代码如下:

 1package com.okayqa.common
2
3import com.fun.config.HttpClientConstant
4import com.fun.frame.httpclient.FanLibrary
5import com.fun.utils.Regex
6import net.sf.json.JSONObject
7import org.apache.http.client.methods.HttpGet
8import org.slf4j.Logger
9import org.slf4j.LoggerFactory
10
11/**
12 * cas服务验证类,主要解决web端登录验证功能
13 */

14class CasCredential extends FanLibrary {
15    static final String OR="/"
16    private static Logger logger = LoggerFactory.getLogger(CasCredential.class)
17    /**
18     * 校验值,随机一次性,从login返回页面中获取
19     */

20    String lt
21    /**
22     * 校验值,随机一次性,从login返回页面中获取,正常值长度在4000+,低于4000请检查请求连接是否传入了回调服务的地址
23     */

24    String execution
25
26/**
27 * 从cas服务的login页面获取到令牌对,此处正则暂时可用,二期会修改表单提交
28 */

29    CasCredential(String host) {
30        def get = getHttpGet(Common.CAS_LOGIN + (host.endsWith(OR) ? host : host + OR))
31        get.addHeader(Common.REQUEST_ID)
32        def response = getHttpResponse(get)
33        def string = response.getString("content")
34        this.lt = Regex.getRegex(string, "<input type=\"hidden\" name=\"lt\" value=\".*?\" />")
35        this.execution = Regex.getRegex(string, " <input type=\"hidden\" name=\"execution\" value=\".*?\" />")
36//        logger.info("cas服务登录host:{},lt:{},execution:{}", host, lt, execution)
37    }
38
39/**
40 * 各个服务端参数一致,由各个服务自己把参数拼好之后传过来,之后在去cas服务拿到令牌对
41 * @param host 服务的host地址,回调由各个服务自己完成,二次验证也是,此处的host不做兼容,有cascredential做处理
42 * @param params 拼好的参数
43 * @return
44 */

45    static JSONObject getTGC(String host, JSONObject params) {
46        def credential = new CasCredential(host)
47        params.put("lt", credential.getLt());
48        params.put("execution", credential.getExecution())
49        params.put("_eventId", "submit");
50        def post = FanLibrary.getHttpPost(Common.CAS_LOGIN + (host.endsWith(OR) ? host : host + OR), params)
51        post.addHeader(Common.REQUEST_ID);
52        FanLibrary.getHttpResponse(post)
53    }
54
55/**
56 * 通过用户
57 * @param url
58 * @return
59 */

60    public static JSONObject verifyST(String url) {
61        HttpGet location = FanLibrary.getHttpGet(url);
62        location.addHeader(Common.REQUEST_ID);
63        JSONObject httpResponse = FanLibrary.getHttpResponse(location);
64        httpResponse.getJSONObject(HttpClientConstant.COOKIE) as JSONObject
65    }
66}

然后顺利完工。因为之前性能测试方案都是使用jmeter作为解决方案,这次架构变更的测试用例难以实现,故才用了脚本。性能框架才用了之前发过的性能测试框架有兴趣的可以点击查看一下,语言以Java为主,脚本使用Groovy写的。

技术类文章精选

非技术文章精选

大咖风采

点击查看公众号地图


原文作者:FunTester;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2019-07-07

  • 博文量
    1037
  • 访问量
    420701