限制在restful PHP API中的操作

I am creating an AngularJS application with a restful API written in PHP as backend. This is the first time I'm using AngularJS and PHP "together".

Angular is keeping track of the authentication of users using the ngCookies module. Some operations, like deleting stuff, should only be available for users with specific privileges. How can I make sure that "normal" users or users that have not logged in cannot access the deletion operations of the API?

Any ideas are appreciated.

Here is how I do it.

  1. In users DB table I add column named token VARCHAR(36)
  2. Whenever user logs in:
  3. I update lastlogin column
  4. I update that token with MD5($ip.$email.$logindate)
  5. Now I return user object to Angular and angular knows token.
  6. In Angular $http service I add interceptors and before any request is made Authentication header is set. I use basic authentication. I create string $user_id.'::'.$token.

    app.factory('authInterceptor', function($rootScope, $q, appConfig, $injector, $cacheFactory) {
        function request(config) {
            if(angular.isDefined($rootScope.currentUser.id)) {
                config.headers.Authorization = 'Basic ' +
                window.btoa($rootScope.currentUser.id + ':' +
                $rootScope.currentUser.token);
            }
    
            return config;
        }
    
        function response(response) {
            if(angular.isDefined(response.data.code) && parseInt(response.data.code) == 401) {
                var UserApi = $injector.get('UserApi');
    
                UserApi.logout();
    
                $cacheFactory.get('$http').removeAll();
    
                UserApi.login(response.data.message)
                    .catch(function() {
                        var $state = $injector.get('$state');
                        $state.go('app.home');
                    });
            }
    
            return response || $q.when(response);
        }
    
        return {
            request:  request,
            response: response
        };
    })
    

This is my authInterceptor factory that I insert into app

app.config(function($httpProvider) {
    $httpProvider.interceptors.push('authInterceptor');
})

What is happening there I set standard Authentication header for every request if user is authorised.

Then in PHP I get this header. I get user ID and Token separately. Then I use user ID to get user data from DB where I have token and last login date.

Now I can compare token and see if this user is the one who logged in.

But this is not absolutely secure. If anyone get this token, then he can login. That is why IP is used. not only I check the token against one in DB I also check it against IP. I create MD5($ip.$email.$logindate) because I know all that data and check against token that I get from angular. If it was sent from different IP it will not pass through.

You can also see function response in authInterceptor. Whenever I have authentication problem I send back HTTP code 401. Now in response I know that Authentication failed. I logout user and redirect him to homepage of the site.

Now it is very simple to code. You just return what have to be returned, and do not care about none authenticated user.

But there is more. If you need some kind of ACL, then you can design this as you wish. In your class that return particular RESTFull API method you can define $acl property and set name of the group. In the same place where you check for authentication, you can check for ACL too.

Please see my code here it is PHP backend and Angular frontend

https://github.com/Coach-Hub

This is the basic Idea, you can of course build around that.

I am not a php developer. You can not ensure this thing in front-end-side. Below is the sample code we use in NodeJs app, to validate user has valid permission of deletion or not.

router.patch('/:id', auth.hasRole(userEnums.roles.admin), controller.update);
router.post('/create', auth.hasRole(userEnums.roles.admin), controller.create);
//Checks if the user role meets the minimum requirements of the route
function hasRole(roleRequired) {
    if (!roleRequired) throw new Error('Required role needs to be set');

    return compose()
        .use(isAuthenticated())
        .use(function meetsRequirements(req, res, next) {
            if (req.user.role.indexOf(roleRequired) !== -1) {
                next();
            }
            else {
                res.send(403);
            }
        });
}

Here auth.hasRole is simple method/middleware, which is a curry design pattern. It checks use req and checks that user has valid permission to delete. In case of user don't have admin permission it return back with unautherize error message. This is for admin and user relation. We also use another strategy at the end to validate user. Suppose we have expose our delete API and anyone can delete it. In that case we have to ensure that active user has to be the owner of the document. In that case first we get the owner id of the document and match it with requested user id. If matches than we delete the document.

BlogPostApiService.destroy(id, _.curry(hasPermission)(req.user))
//hasPermission implementation
function hasPermission(user, blogPost) {
    return user && (user.hasRole(UserEnums.roles.admin) || (user._id.toString() == blogPost.author.id.toString()));
}