江湖險惡,我從來都不輕易留下我的姓名。

憑你的智慧,我唬得了你嗎?

Sails練習之十一 - 使用者認證以及嚴格存取

| Comments

本章的Video為 Ep14 - User authentication and restricting access through policies.

首先,我們要先將前一章的Session的練習的程式碼先拿掉,

接著修改SessionController.js, 新增create action

SessionController.js新增Create action
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
87
88
89
90
91
92
93
94
95
96
var bcrypt = require('bcrypt');

module.exports = {
    'new': function(req, res) {
        res.view('session/new');
    },
    create: function(req, res, next) {

        // Check for email and password in params sent via the form, if none
        // redirect the browser back to the sign-in form.
        if (!req.param('email') || !req.param('password')) {
            // return next({err: ["Password doesn't match password confirmation."]});

            var usernamePasswordRequiredError = [{
                name: 'usernamePasswordRequired',
                message: 'You must enter both a username and password.'
            }]

            // Remember that err is the object being passed down (a.k.a. flash.err), whose value is another object with
            // the key of usernamePasswordRequiredError
            req.session.flash = {
                err: usernamePasswordRequiredError
            }

            res.redirect('/session/new');
            return;
        }

        // Try to find the user by there email address.
        // findOneByEmail() is a dynamic finder in that it searches the model by a particular attribute.
        // User.findOneByEmail(req.param('email')).done(function(err, user) {
        User.findOneByEmail(req.param('email'), function foundUser(err, user) {
            if (err) return next(err);

            // If no user is found...
            if (!user) {
                var noAccountError = [{
                    name: 'noAccount',
                    message: 'The email address ' + req.param('email') + ' not found.'
                }]
                req.session.flash = {
                    err: noAccountError
                }
                res.redirect('/session/new');
                return;
            }

            // Compare password from the form params to the encrypted password of the user found.
            bcrypt.compare(req.param('password'), user.encryptedPassword, function(err, valid) {
                if (err) return next(err);

                // If the password from the form doesn't match the password from the database...
                if (!valid) {
                    var usernamePasswordMismatchError = [{
                        name: 'usernamePasswordMismatch',
                        message: 'Invalid username and password combination.'
                    }]
                    req.session.flash = {
                        err: usernamePasswordMismatchError
                    }
                    res.redirect('/session/new');
                    return;
                }

                // Log user in
                req.session.authenticated = true;
                req.session.User = user;

                // Change status to online
                user.online = true;
                user.save(function(err, user) {
                    if (err) return next(err);

                    // Inform other sockets (e.g. connected sockets that are subscribed) that this user is now logged in
                    User.publishUpdate(user.id, {
                        loggedIn: true,
                        id: user.id,
                        name: user.name,
                        action: ' has logged in.'
                    });

                    // If the user is also an admin redirect to the user list (e.g. /views/user/index.ejs)
                    // This is used in conjunction with config/policies.js file
                    if (req.session.User.admin) {
                        res.redirect('/user');
                        return;
                    }

                    //Redirect to their profile page (e.g. /views/user/show.ejs)
                    res.redirect('/user/show/' + user.id);
                });
            });
        });
    }

};

新增完成之後, 重新啟動sails, (記得啟動sails之前要先啟動Mongo DB), 然後試試看功能

到瀏覽器輸入http://localhost:1337/session/new

然後輸入不正確的帳號密碼以及正確的帳號密碼試試看, 應該就沒問題了

另外, 關於Create的每一個判斷式, 就請參考Irl nathan本人的講解嚕

接著我們要在SessionController.js中加入destroy, 也就是Sign Out的Action

SessionController.js 新增Destroy action
1
2
3
4
5
6
7
8
destroy: function(req, res, next) {

    // Wipe out the session (log out)
    req.session.destroy();

    // Redirect the browser to the sign-in screen
    res.redirect('/session/new');
}

把上面的程式碼加到Create Action之後, 然後重啟sails,

再登入之後, 直接輸入http://localhost:1337/session/destroy 就會跳回到登入頁面

不過我們可以發現, 就算登出了, 使用者還是可以直接輸入網址, 去瀏覽使用者,

例如在瀏覽器中輸入http://localhost:1337/user 一樣可以看到管理介面

所以我們要修改一些權限, 來達成管控的要求

先到activeityOverload\api\policies\底下新增authenticated.js, 程式碼如下

authenticated.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
module.exports = function (req, res, ok) {

    // User is allowed, proceed to controller
    if (req.session.User) {
        return ok();
    }
    else {  // User is not allowed
        var requireLoginError = [{name: 'requireLogin', message: 'You must be signed in.'}]
        req.session.flash = {
            err: requireLoginError
        }
        res.redirect('/session/new');
        return;
        //res.send(403);
    }
};

然後到activeityOverload\config\底下的policies.js

新增以下的規則

policies.js
1
2
3
4
5
 '*': 'flash',
 user:{
        'new': "flash",
        '*': "authenticated"
    }

解釋一下上面的Policies, 第一列的 * 表示 不管哪一個Controller都要經過flash規則

而第二列的是說, 針對user controller, 底下的new action我們套用flash, 而其他的action則套用authenticated這個規則.

所以只要是user底下的非new的action, 都要經過authenticated這個關卡. 大致上是這樣

新增完成之後. 重新啟動sails, 看看若我們直接輸入localcost:1337/user/, 若我們之前沒有登入, 則在authenticated.js中, req.session.User就會是空的, 因為是空, 所以是 null, 那麼邏輯就會走到else, 有就是User is not allowd的這判斷式中.

以上, 就是我們這一堂課的概要嚕, 但還沒結束, 作者在這一集Building a Sails Application: Ep16 - Fixing an issue with policies in episode 14

找到了一個要在本章修正的錯誤, 當我們在config\policies.js新增了filter rule之後, 會發現新增使用者之後, 到了create action, 也會套用到authenticated rule, 導致錯誤發生, 所以我們修正如下

config\policies.js
1
2
3
4
5
6
'*': 'flash',
user:{
    'new': "flash",
    'create': "flash",
    '*': "authenticated"
}

在user controller新增一個create action套用flash的rule即可

另外到UserController.js, 在create的action中新加入兩段codes

UserController.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 create: function (req, res, next) {
        User.create(req.params.all(), function userCreated(err, user) {
            //if(err) return next(err);

            if (err) {
                console.log(err);
                req.session.flash = {
                    err: err
                }
                //if error redirect back to sign-up page
                return res.redirect('/user/new');
            }

            //res.json(user);
            //req.session.flash = {};
          //新增底下這兩段code
            req.session.authenticated = true;
            req.session.User = user;
            res.redirect('/user/show/' + user.id);
        });
    },

OK. 這樣應該就沒問題了

Comments