基本信息 
语言/知识点 cas、python 
博客时间 2023.10.12 
 
 
结果 分析统一身份认证登录过程 
 
背景 每次校园网内登录办事大厅或者在浏览IEEE期刊时,都会要求验证学生身份,此时都是使用深圳大学统一身份认证来进行学生认证。
网络包分析登录过程 F12查看身份认证过程
实现内网CAS统一认证登录 
第一个请求 首次访问web服务(GET一个校内url)时返回了一个302状态(即重定向),重定向的地址在响应标头的Location域中指出。查看location的值,可以确认重定向到了cas服务的登录界面,且带了一个service参数,指明了浏览器原访问的服务站点(为了cas认证后正确跳转回去)
1 2 3 4 response = self.svr_session.get(url=serviceUrl, allow_redirects=False ) self.cas_url = response.headers["location" ] self.cas_url = self.cas_url1 + self.cas_url print (self.cas_url)
此时cas_url为重定向到cas服务登录界面的url
第二个请求(认证页面的第一个请求) 标头数据如下: 请求URL:https://authserver.szu.edu.cn/authserver/login?service=https%3A%2F%2Fehall.szu.edu.cn%3A443%2Flogin%3Fservice%3Dhttps%3A%2F%2Fehall.szu.edu.cn%2Fnew%2Findex.html 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding:gzip, deflate, br Accept-Language:zh-CN,zh;q=0.9 Cache-Control:max-age=0 Connection:keep-alive Content-Length:273 Content-Type:application/x-www-form-urlencoded Cookie:route=091bbd9a6c8e2a18c423b0accfefdcd7; JSESSIONID_auth=P-MiqZzuK3EmeGNt8MmBVV0NOCddkqhnKX55SpEZDcJcFD4isXnf!778334097; org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE=zh_CN Dnt:1 Host:authserver.szu.edu.cn Origin:https://authserver.szu.edu.cn Referer:https://authserver.szu.edu.cn/authserver/login?service=https%3A%2F%2Fehall.szu.edu.cn%3A443%2Flogin%3Fservice%3Dhttps%3A%2F%2Fehall.szu.edu.cn%2Fnew%2Findex.html Sec-Ch-Ua:"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117" Sec-Ch-Ua-Mobile:?0 Sec-Ch-Ua-Platform:"Windows" Sec-Fetch-Dest:document Sec-Fetch-Mode:navigate Sec-Fetch-Site:same-origin Sec-Fetch-User:?1 Upgrade-Insecure-Requests:1 User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60 
负载部分如下: 查询字符串参数:
1 service :  https://ehall.szu.edu.cn:443/login?service=https://ehall.szu.edu.cn/new/index.html
表单数据:
1 2 3 4 5 6 7 username :  2210111111password :  MukNAJb1ti4x93ujn0jcK5KaxdVSjsoOAJb1ti4x93ujn0jcK5KaAJb1ti4x93ujn0jcK5KaxdVSjsoOAJb1ti4x93SjsAJb1ti4x93ujn0jcK5KaVSjsoO=lt :  LT-412239-fBpb6E51MWcxpQvfsTwcrdM7jGsqA31697093632120-SjdO-casdllt :  userNamePasswordLoginexecution :  e2s1_eventId: submit rmShown :  1
响应标头如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 Cache-Control:no-cache Cache-Control:no-store Connection:keep-alive Content-Security-Policy:upgrade-insecure-requests;connect-src * Date:Thu, 12 Oct 2023 07:01:24 GMT Expires:Thu, 01 Jan 1970 00:00:00 GMT Location:https://ehall.szu.edu.cn:443/login?service=https://ehall.szu.edu.cn/new/index.html&ticket=ST-68862-useghL7Xv2VNpNRKubht1697094085223-sYAt-cas Pragma:no-cache Server:openresty Set-Cookie:CASPRIVACY=; expires=Thu, 01-Jan-1970 01:00:00 GMT; path=/authserver/ Set-Cookie:CASTGC=TGT-21173-LbpXoJ5abPpFLNROzTfZB6RZQMJbXJJyYhtTOB6bM16wz4oCeK1697094085206-NYTe-cas;Path=/authserver/;HttpOnly;secure; Set-Cookie:iPlanetDirectoryPro=9pRNN5LxbxDRw7FXc3N6w6; domain=.nju.edu.cn; path=/ Transfer-Encoding:chunked 
分析: 可以看到发送了一个POST请求,请求正文里存在用户名,密码。然而除了用户名密码外,还有几个不知道哪里来的字段_eventId、execution、lt、dllt、rmShown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 function  getAesString (data, key0, iv0 ) {  key0 = key0.replace (/(^\s+)|(\s+$)/g , "" );   var  key = CryptoJS .enc .Utf8 .parse (key0);   var  iv = CryptoJS .enc .Utf8 .parse (iv0);   var  encrypted = CryptoJS .AES .encrypt (data, key, {     iv : iv,     mode : CryptoJS .mode .CBC ,     padding : CryptoJS .pad .Pkcs7    });   return  encrypted.toString (); } function  encryptAES (data, aesKey ) {  if  (!aesKey) {     return  data;   }   var  encrypted = getAesString (     randomString (64 ) + data,     aesKey,     randomString (16 )   );   return  encrypted; } function  encryptPassword (pwd0, key ) {  try  {     return  encryptAES (pwd0, key);   } catch  (e) {}   return  pwd0; } var  $aes_chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678" ;var  aes_chars_len = $aes_chars.length ;function  randomString (len ) {  var  retStr = "" ;   for  (i = 0 ; i < len; i++) {     retStr += $aes_chars.charAt (Math .floor (Math .random () * aes_chars_len));   }   return  retStr; } 
把代码的其他部分注释掉可以发现并不影响函数,所以我们只需要搞明白这段代码的逻辑,然后在pyhton中复现即可。
编码实现: 加密部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 from  Crypto.Cipher import  AESimport  binasciiimport  randomdef  get_aes_string (data, key0, iv0 ):    key0 = key0.strip()     key = key0.encode('utf-8' )     iv = iv0.encode('utf-8' )     cipher = AES.new(key, AES.MODE_CBC, iv)     data = data.encode('utf-8' )          padding = 16  - (len (data) % 16 )     data += bytes ([padding]) * padding     encrypted = cipher.encrypt(data)     return  binascii.b2a_base64(encrypted).decode('utf-8' ) def  encrypt_aes (data, aes_key ):    if  not  aes_key:         return  data     random_str = '' .join(random.choice("ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678" ) for  _ in  range (64 ))     combined_data = random_str + data     iv = '' .join(random.choice("ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678" ) for  _ in  range (16 ))     encrypted = get_aes_string(combined_data, aes_key, iv)     return  encrypted def  encrypt_password (pwd0, key ):    try :         return  encrypt_aes(pwd0, key)     except  Exception as  e:         pass      return  pwd0 
登录部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 import  requestsfrom  lxml import  etreeimport  reimport  osimport  pickleimport  pyscriptfrom  http.cookies import  SimpleCookiefrom  encrypt import  encrypt_passwordfrom  requests.cookies import  create_cookieclass  casService (object ):    def  __init__ (self, svr_session ):         self.cas_url1 = "https://authserver.szu.edu.cn/authserver"          self.svr_session = svr_session           self.session = requests.session()                    self.headers = {"Accept" : "text/html, application/xhtml+xml, application/xml; q=0.9, /; q=0.8" ,                         "Accept-Language" : "zh_CN" , "Connection" : "keep-alive" ,                         "User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363" , }     def  Login (self, serviceUrl="" , username=None , password=None  ):         response = self.svr_session.get(url=serviceUrl, allow_redirects=False )         if  response.status_code == 200 :             return  True          self.cas_url = response.headers["location" ]         self.cas_url = self.cas_url1 + self.cas_url         print (self.cas_url)         cas_response = self.session.get(self.cas_url, allow_redirects=False )         if  cas_response.status_code == 200 :               if  username == None  or  password == None :                 print ("cas_cookie not valid" )                 username = input ("plase input username:" )                 password = input ("plase input password:" )             loginhtml = etree.HTML(cas_response.text)                          with  open ("loginhtml.html" , 'w' , encoding='utf-8' ) as  f:                 f.write(cas_response.text)             lt_value = loginhtml.xpath(                 "//input[@name='lt']/@value" )               dllt_value = loginhtml.xpath("//input[@name='dllt']/@value" )               execution_value = loginhtml.xpath("//input[@name='execution']/@value" )               _eventId_value = loginhtml.xpath("//input[@name='_eventId']/@value" )               rmShown_value = loginhtml.xpath("//input[@name='rmShown']/@value" )               pwdDefaultEncryptSalt_value = loginhtml.xpath(                 "//input[@id='pwdDefaultEncryptSalt']/@value" )               password = encrypt_password(password, pwdDefaultEncryptSalt_value[0 ])             auth_data = {                 "username" : username,                 "password" : password,                 "lt" : lt_value[0 ],                 "dllt" : dllt_value[0 ],                 "execution" : execution_value[0 ],                 "_eventId" : _eventId_value[0 ],                 "rmShown" : rmShown_value[0 ],             }             auth_response = self.session.post(self.cas_url, data=auth_data, allow_redirects=False )             if  auth_response.status_code == 302 :                 url_with_ticket = auth_response.headers["location" ]                 confirm_response = self.svr_session.get(url=url_with_ticket, allow_redirects=False )                                  print (confirm_response.headers)                 location = confirm_response.headers["location" ]                 ticket = re.findall(r"ticket=(.*)" , url_with_ticket)                 JSESSIONID_auth = re.findall(r"JSESSIONID_auth=(.*);" , str (confirm_response.headers))                 return  ticket, JSESSIONID_auth, location svr_session = requests.session() cas = casService(svr_session) your_username = "123456"  your_password = "12345678"  ticket, jsessionid, location = cas.Login(serviceUrl="https://ehall.szu.edu.cn/appShow?appId=4997580036338493" ,                                username=your_username, password=your_password) 
第三个请求(认证页面的第二个请求) 标头数据如下: 请求URL:https://ehall.szu.edu.cn/login?service=https://ehall.szu.edu.cn/new/index.html&ticket=ST-61160-a7XFPjfZK1J1mZctUNDa1697096758336-ntS3-cas 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding:gzip, deflate, br Accept-Language:zh-CN,zh;q=0.9 Cache-Control:max-age=0 Connection:keep-alive Cookie:route=4ef83fbd3cb79c7b0dd7289570453777; amp.locale=undefined; JSESSIONID=79YiyouLxOG199bVCD2tEhupwyy_NNjHjBhkNYD3UvEON7M7dJu6!840984734 Dnt:1 Host:ehall.szu.edu.cn Referer:https://authserver.szu.edu.cn/ Sec-Ch-Ua:"Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117" Sec-Ch-Ua-Mobile:?0 Sec-Ch-Ua-Platform:"Windows" Sec-Fetch-Dest:document Sec-Fetch-Mode:navigate Sec-Fetch-Site:same-site Sec-Fetch-User:?1 Upgrade-Insecure-Requests:1 User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60 
负载部分如下: 查询字符串参数:
1 2 service :  https://ehall.szu.edu.cn/new/index.htmlticket :  ST-61160-a7XFPjfZK1J1mZctUNDa1697096758336-ntS3-cas
响应标头如下: 1 2 3 4 5 6 7 Connection:keep-alive Content-Length:142 Content-Type:text/html Date:Thu, 12 Oct 2023 07:45:59 GMT Location:https://ehall.szu.edu.cn:443/login?service=https://ehall.szu.edu.cn/new/index.html Server:openresty Set-Cookie:MOD_AUTH_CAS=MOD_AUTH_ST-61160-a7XFPjfZK1J1mZctUNDa1697096758336-ntS3-cas; path=/; Httponly 
分析: 可以看到发送了一个GET请求,请求正文是空的https://ehall.szu.edu.cn:443/login?service=https://ehall.szu.edu.cn/new/index.html),并设置cookie(Set-Cookie:MOD_AUTH_CAS=MOD_AUTH_ST-61160-a7XFPjfZK1J1mZctUNDa1697096758336-ntS3-cas; path=/; Httponly)。
第四个请求(认证页面的第三个请求/返回重定向地址指向最初请求的资源) 分析: 
第五个请求(认证页面的第四个请求/进入办事大厅) 分析: 
参考链接 实现内网CAS统一认证登录