使用 Android 登录时未设置 Passport.js 会话

Posted

技术标签:

【中文标题】使用 Android 登录时未设置 Passport.js 会话【英文标题】:Passport.js sessions are not set when Login with Android 【发布时间】:2016-12-04 03:33:51 【问题描述】:

在我的 AuthController.js 中,我检查登录是否成功并在会话中设置哈希码:

 req.logIn(user, function (err) 
                if (err) res.send(err);
                var redirectTo = req.session.redirectTo ? req.session.redirectTo : '/user/show/'+user.id;
                delete req.session.redirectTo;

                bcrypt.genSalt(10, function (err, salt) 
                    bcrypt.hash(user.email, salt, function (err, hash) 
                        if (err) 
                            console.log(err);
                         else 


                            req.session.passport.user_type = user.type;
                            req.session.passport.user_avatar = user.avatar;
                            req.session.passport.email = user.email;
                            req.session.passport.token = hash;

                             // here session is set! also when login with android 
                                console.log("User passport Sessions: ",req.session.passport)

                                res.json(user);
                                res.end();

                        
                    );
                )

            )

当我使用浏览器登录时设置会话 - 我使用另一个简单的控制器检查它们:

'who': function (req, res) 
        res.json(req.session);
    ,

当我使用我的 Android 应用程序(Volley 请求)登录时,一切都按预期工作,但是当我在成功登录后向这个“谁”控制器发出新请求时,整个“护照”属性为空。

为什么我使用 Android App 登录时没有设置护照会话?

问题是:凌空请求与浏览器请求不同吗?当服务器无法为 volley 请求保存会话时,我如何在服务器上知道(在第二个请求中)用户已经登录?

我转储了 req.headers,这就是 Chrome 的样子:

 host: 'localhost:1337',
  connection: 'keep-alive',
  'cache-control': 'max-age=0',
  'upgrade-insecure-requests': '1',
  'user-agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/51.0.2704.103 Safari/537.36',
  accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
  'accept-encoding': 'gzip, deflate, sdch',
  'accept-language': 'de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4',
  cookie: '__utma=111872281.1508283337.1455636609.1455636609.1455636609.1; __utmz=111872281.1455636609.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _ga=GA1.1.1508283337.1455636609; sails.sid=s%3AHNW3D3ktwA79IFHH4gX9Ko6o73MZOjRK.I7sTYkCKSkkwset6OC2ap58fcROPtV6PqUnkaInGW44',
  'if-none-match': 'W/"1f3e-WoreHgYUy3uvXGNH++ttsQ"' 

这就是 Android 请求通过 Volley 的样子:

 'user-agent': 'Dalvik/2.1.0 (Linux; U; Android 6.0; Android SDK built for x86 Build/MASTER)',
  host: '10.0.2.2:1337',
  connection: 'Keep-Alive',
  'accept-encoding': 'gzip' 
 'if-none-match': 'W/"1f3e-WoreHgYUy3uvXGNH++ttsQ"',
  'user-agent': 'Dalvik/2.1.0 (Linux; U; Android 6.0; Android SDK built for x86 Build/MASTER)',
  host: '10.0.2.2:1337',
  connection: 'Keep-Alive',
  'accept-encoding': 'gzip' 

缺少会话部分 - 同时我知道您必须自己实现会话管理。是否有针对 Volley 上的多个 HTTP 请求的简单教程?

【问题讨论】:

【参考方案1】:

好吧 10 天后我知道如何“解决”这个问题。

当你向你的服务器发出请求时,你会得到一堆东西。标题对我们来说很重要。

在此处阅读有关 HTTP 标头的更多信息: http://code.tutsplus.com/tutorials/http-headers-for-dummies--net-8039

HTTP 请求是无状态的,也就是说,它不保存有关客户端的信息和状态。

Volley 不处理头球。我们要做的是:

当我们从 App 发出第一个请求时,我们必须保存响应头信息。

"Access-Control-Allow-Credentials":"","Access-Control-Allow-Headers":"","Access-Control-Allow-Methods":"","Access-Control-Allow-Origin":"","Access-Control-Expose-Headers":"","Connection":"keep-alive","Content-Length":"235","Content-Type":"application\/json; charset=utf-8","Date":"Tue, 09 Aug 2016 22:39:40 GMT","ETag":"W\/\"eb-bxZXjFIir0ihzYNPgoUbTg\"","set-cookie":"sails.sid=s%3AsjQL1FsAJY5VWGHdgChpa0hgZPs06AH9z.H1RLNh%2FMqLjxdGtKtnWFv8cFO8WjeAUhPFQp2Yl73Lo; Path=\/; HttpOnly","Vary":"X-HTTP-Method-Override","X-Android-Received-Millis":"1470713694296","X-Android-Response-Source":"NETWORK 200","X-Android-Selected-Protocol":"http\/1.1","X-Android-Sent-Millis":"1470713694119","X-Powered-By":"Sails <sailsjs.org>"

标题中的重要信息是“set-cookie”部分。我们必须从第一次调用中保存这些信息,并在下一个请求中重复使用它,以便服务器知道我们是谁。

怎么做?

我使用了来自 djodjo 的自定义请求。重要的方法是 parseNetworkResponse: https://***.com/a/36435582/3037960

我们像这样使用这个类:

mHeaders = new HashMap<String, String>(); 
    session = new SessionManager(myView.getContext());

 registerRequest = new MetaRequest(1, MY_ENDPOINT, obj, new Response.Listener<JSONObject>() 

                    @Override
                        public void onResponse(JSONObject response) 

                            JSONObject headers = new JSONObject();
                            try 
                                headers.put("headers", response.get("headers"));
   // get the "set-cookie" value
                                String sailsSession = String.valueOf(headers.getJSONObject("headers").get("set-cookie"));
                                Log.d("Response Headers", sailsSession);

                                // Session Manager
                                session.storeSailsSession(sailsSession);

                                String cookie = session.getSailsSession();

                                if( cookie != null) 
                                   //just to test if the cookie is now saved?
                                    Log.d("Cookies", cookie);
                                

                             catch (JSONException e) 
                                e.printStackTrace();
                            

                        
                    , new Response.ErrorListener() 

                        @Override
                        public void onErrorResponse(VolleyError error) 
                                Log.d("ERROR", String.valueOf(error));
                            // TODO Auto-generated method stub

                        
                    )

                        public Map<String, String> getHeaders() 
                            return mHeaders;
                        
                    ;

                    RequestQueue queue = Volley.newRequestQueue(getActivity());
                    queue.add(registerRequest);

你看到那里有会话管理器... session.storeSailsS​​ession(sailsS​​ession); 这是一个自定义类,用于将我们的 cookie 保存在共享首选项中,以便我们可以获取此信息当我们需要它们时,在应用程序中整体显示。

public class SessionManager 
    // Shared Preferences
    SharedPreferences pref;

    // Editor for Shared preferences
    Editor editor;

    // Context
    Context _context;


    // User hash/cookie
    public static final String KEY_SESSION = "session";

    // Constructor
    public SessionManager(Context context)
        this._context = context;
        pref = _context.getSharedPreferences(PREF_NAME, 0);
        editor = pref.edit();
    


    public void storeSailsSession(String session)
        editor.putString(KEY_SESSION,session);
        editor.commit();

       
 public String getSailsSession()
        return pref.getString(KEY_SESSION,null);
    

    

您现在了解如何从标头中获取 cookie,如何存储它们以备后用;现在对于下一个请求如何设置标头?

在发送请求之前(您不必使用上面的自定义 MetaRequest),您必须从共享首选项中获取会话,然后在哈希映射 mHeaders 中设置它:

String sailsCookie =  session.getSailsSession();
mHeaders.put("cookie", sailsCookie.toString());

并且不要忘记将 mHeaders 返回到您的请求中,如下所示:

JsonArrayRequest arrayreq = new JsonArrayRequest(JsonURL,Listener,ErrorListener)
                public Map<String, String> getHeaders() 
                    return mHeaders;
                

通常你只使用 new WhatEverRequest(....); 但现在你必须附加 ... return mHeaders;

希望你能理解,否则就发表评论,我会解释的。对我来说它有效,所以我相信它也适用于你。下次推荐你使用https://jwt.io

【讨论】:

我又来了,1.5 个月后:使用改造!改造就像 1000 倍容易和更好 - 我希望我知道它futurestud.io/tutorials/retrofit-add-custom-request-header

以上是关于使用 Android 登录时未设置 Passport.js 会话的主要内容,如果未能解决你的问题,请参考以下文章

登录服务器时未登录服务器

直接访问登录表单时未设置会话 cookie,导致 CSRF 令牌无效

Bootstrap Modal 在其他按钮上设置时未打开

swift - xcode 11中使用frame = bound选项时未对齐

Flutter:登录 Instagram 时未获取代码(Instagram 登录不起作用)

如何解决护照认证产生的错误