AngularJS: ตัวอย่างพื้นฐานในการใช้การพิสูจน์ตัวตนใน Single Page Application


100

ฉันยังใหม่กับAngularJSและได้อ่านบทแนะนำของพวกเขาและรู้สึกได้ถึงมัน

ฉันมีแบ็กเอนด์สำหรับโปรเจ็กต์ของฉันพร้อมที่RESTจะต้องตรวจสอบความถูกต้องของอุปกรณ์ปลายทางแต่ละจุด

สิ่งที่ฉันต้องการจะทำอย่างไร
ได้.) http://myproject.comฉันต้องการจะมีหน้าเดียวสำหรับโครงการของฉัน
ข.) เมื่อผู้ใช้ฮิต URL ในเบราว์เซอร์บนพื้นฐานถ้าผู้ใช้เข้าสู่ระบบหรือไม่ได้เขาจะนำเสนอกับหน้าบ้าน / ดูหรือหน้าเข้าสู่ระบบ / มุมมองภายใต้ http://myproject.comURL
c.) หากผู้ใช้ไม่ได้เข้าสู่ระบบผู้ใช้จะกรอกแบบฟอร์มและเซิร์ฟเวอร์จะตั้งค่าUSER_TOKENในเซสชันดังนั้นคำขอเพิ่มเติมไปยังปลายทางทั้งหมดจะได้รับการรับรองความถูกต้องตามUSER_TOKEN

My Confusions
a.) ฉันจะจัดการกับการพิสูจน์ตัวตนฝั่งไคลเอ็นต์โดยใช้ AngularJS ได้อย่างไร? ฉันเห็นที่นี่และที่นี่แต่ไม่เข้าใจวิธีใช้
b.) ฉันจะนำเสนอมุมมองที่แตกต่างกันให้กับผู้ใช้ได้อย่างไรโดยขึ้นอยู่กับว่าผู้ใช้ล็อกอินหรือไม่อยู่ภายใต้ url เดียวกันhttp://myproject.com

ฉันใช้ angular.js เป็นครั้งแรกและเริ่มสับสนจริงๆว่าจะเริ่มอย่างไร คำแนะนำและ / หรือแหล่งข้อมูลใด ๆ จะได้รับการชื่นชมอย่างมาก


โปรดดูบทความด้านล่างfrederiknakstad.com/…
Ajay Beniwal

1
@MichaelCalkins เพียงแค่วางลิงค์ไม่สร้างสรรค์ อย่างน้อยคุณควรบอกว่าลิงก์จะให้อะไร
Dave Gordon

My b: AngularJS Access Control and Authentication coderwall.com/p/f6brkg
Michael J. Calkins

ทีมงานที่ OAuth มีห้องสมุดที่ยอดเยี่ยมสำหรับandreareginato.github.io/oauth-ng
Faktor 10

คำตอบ:


48

ฉันได้สร้าง github repo สรุปบทความนี้โดยพื้นฐาน: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login Github repo

Plunker

ฉันจะพยายามอธิบายให้ดีที่สุดหวังว่าฉันจะช่วยพวกคุณได้บ้าง:

(1) app.js:การสร้างค่าคงที่ในการตรวจสอบความถูกต้องของข้อกำหนดของแอป

var loginApp = angular.module('loginApp', ['ui.router', 'ui.bootstrap'])
/*Constants regarding user login defined here*/
.constant('USER_ROLES', {
    all : '*',
    admin : 'admin',
    editor : 'editor',
    guest : 'guest'
}).constant('AUTH_EVENTS', {
    loginSuccess : 'auth-login-success',
    loginFailed : 'auth-login-failed',
    logoutSuccess : 'auth-logout-success',
    sessionTimeout : 'auth-session-timeout',
    notAuthenticated : 'auth-not-authenticated',
    notAuthorized : 'auth-not-authorized'
})

(2) Auth Service:ฟังก์ชั่นต่อไปนี้ทั้งหมดถูกนำไปใช้ในบริการ auth.js บริการ $ http ใช้เพื่อสื่อสารกับเซิร์ฟเวอร์สำหรับขั้นตอนการพิสูจน์ตัวตน ยังมีฟังก์ชันเกี่ยวกับการอนุญาตนั่นคือหากผู้ใช้ได้รับอนุญาตให้ดำเนินการบางอย่าง

angular.module('loginApp')
.factory('Auth', [ '$http', '$rootScope', '$window', 'Session', 'AUTH_EVENTS', 
function($http, $rootScope, $window, Session, AUTH_EVENTS) {

authService.login() = [...]
authService.isAuthenticated() = [...]
authService.isAuthorized() = [...]
authService.logout() = [...]

return authService;
} ]);

(3) เซสชัน:ซิงเกิลเพื่อเก็บข้อมูลผู้ใช้ การใช้งานที่นี่ขึ้นอยู่กับคุณ

angular.module('loginApp').service('Session', function($rootScope, USER_ROLES) {

    this.create = function(user) {
        this.user = user;
        this.userRole = user.userRole;
    };
    this.destroy = function() {
        this.user = null;
        this.userRole = null;
    };
    return this;
});

(4) ตัวควบคุมหลัก:ถือว่านี่เป็นฟังก์ชั่น "หลัก" ของแอปพลิเคชันของคุณตัวควบคุมทั้งหมดจะสืบทอดมาจากคอนโทรลเลอร์นี้และเป็นกระดูกสันหลังของการตรวจสอบสิทธิ์ของแอปนี้

<body ng-controller="ParentController">
[...]
</body>

(5) การควบคุมการเข้าถึง:ในการปฏิเสธการเข้าถึงบางเส้นทางจะต้องดำเนินการ 2 ขั้นตอน:

a) เพิ่มข้อมูลของบทบาทที่อนุญาตให้เข้าถึงแต่ละเส้นทางบนบริการ $ stateProvider ของเราเตอร์ ui ดังที่เห็นด้านล่าง (สามารถใช้ได้กับ ngRoute)

.config(function ($stateProvider, USER_ROLES) {
  $stateProvider.state('dashboard', {
    url: '/dashboard',
    templateUrl: 'dashboard/index.html',
    data: {
      authorizedRoles: [USER_ROLES.admin, USER_ROLES.editor]
    }
  });
})

b) ใน $ rootScope $ on ('$ stateChangeStart') เพิ่มฟังก์ชันเพื่อป้องกันการเปลี่ยนสถานะหากผู้ใช้ไม่ได้รับอนุญาต

$rootScope.$on('$stateChangeStart', function (event, next) {
    var authorizedRoles = next.data.authorizedRoles;
    if (!Auth.isAuthorized(authorizedRoles)) {
      event.preventDefault();
      if (Auth.isAuthenticated()) {
        // user is not allowed
        $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
      } else {
        // user is not logged in
        $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
      }
    }
});

(6) Auth interceptor:ใช้งานได้ แต่ไม่สามารถตรวจสอบขอบเขตของรหัสนี้ได้ หลังจากคำขอ $ http แต่ละครั้งผู้สกัดกั้นนี้จะตรวจสอบรหัสสถานะหากมีการส่งคืนรายการใดรายการหนึ่งด้านล่างนี้ระบบจะถ่ายทอดเหตุการณ์เพื่อบังคับให้ผู้ใช้เข้าสู่ระบบอีกครั้ง

angular.module('loginApp')
.factory('AuthInterceptor', [ '$rootScope', '$q', 'Session', 'AUTH_EVENTS',
function($rootScope, $q, Session, AUTH_EVENTS) {
    return {
        responseError : function(response) {
            $rootScope.$broadcast({
                401 : AUTH_EVENTS.notAuthenticated,
                403 : AUTH_EVENTS.notAuthorized,
                419 : AUTH_EVENTS.sessionTimeout,
                440 : AUTH_EVENTS.sessionTimeout
            }[response.status], response);
            return $q.reject(response);
        }
    };
} ]);

ป.ล.ข้อบกพร่องของการป้อนข้อมูลแบบฟอร์มอัตโนมัติตามที่ระบุไว้ในบทความที่ 1 สามารถหลีกเลี่ยงได้อย่างง่ายดายโดยการเพิ่มคำสั่งที่รวมอยู่ใน directives.js

PS2รหัสนี้สามารถปรับแต่งได้อย่างง่ายดายโดยผู้ใช้เพื่อให้สามารถมองเห็นเส้นทางต่างๆหรือแสดงเนื้อหาที่ไม่ได้ตั้งใจให้แสดง ต้องใช้ตรรกะทางฝั่งเซิร์ฟเวอร์นี่เป็นเพียงวิธีแสดงสิ่งต่าง ๆ อย่างถูกต้องบน ng-app ของคุณ


1
ฉันทำตามคำแนะนำของคุณเพื่อให้เข้าใจตรรกะฝั่งไคลเอ็นต์ มันดีจริงๆ !! ฉันพลาดบางอย่างเกี่ยวกับการทำลายเซสชันด้วยตนเอง แต่เราต้องทดลองและทำลายสิ่งต่างๆเช่นกัน!
Sebastialonso

~~ ไม่แน่ใจว่าฉันเข้าใจบรรทัดauthService.login() = [...]นั้นถูกต้องหรือไม่: วงเล็บเหลี่ยมเหล่านั้นจะใช้แทนอะไรได้$http.get(url, {uID, pwd}หรือไม่ ~~ โอเคมองเข้าไปใน plunker มันเป็นอย่างที่ฉันพูด XD
netalex

1
คุณช่วยขยายคำตอบสำหรับฝั่งเซิร์ฟเวอร์ได้ไหม
สอบถาม

25

ฉันชอบแนวทางนี้และนำไปใช้บนฝั่งเซิร์ฟเวอร์โดยไม่ต้องทำสิ่งที่เกี่ยวข้องกับการตรวจสอบสิทธิ์ในส่วนหน้า

'เทคนิค' ของฉันในแอปล่าสุดของฉันคือ .. ลูกค้าไม่สนใจเรื่อง Auth ทุกสิ่งในแอปต้องมีการเข้าสู่ระบบก่อนดังนั้นเซิร์ฟเวอร์จะแสดงหน้าเข้าสู่ระบบเสมอเว้นแต่จะตรวจพบผู้ใช้ที่มีอยู่ในเซสชัน หากพบ session.user เซิร์ฟเวอร์จะส่ง index.html แบม: -o

มองหาความคิดเห็นของ "Andrew Joslin"

https://groups.google.com/forum/?fromgroups=#!searchin/angular/authentication/angular/POXLTi_JUgg/VwStpoWCPUQJ


3
ถ้ามันเป็นเว็บ api? ฉันไม่ได้รับคำตอบของคุณฉันเดา :(
Leandro De Mello Fagundes

1
ถ้าคุณต้องการแสดงชื่อผู้ใช้ล่ะ? หรือหากคุณกำลังคุยกับบริการที่มีชื่อผู้ใช้ใน URL ปลายทาง
perrygeo

2
ขอโทษ แต่ฉันไม่เข้าใจคำตอบ คุณจัดการกับเซสชันในเชิงมุมอย่างไร session.user set อยู่ที่ไหน คุณช่วยสร้างตัวอย่างโค้ดของสิ่งนี้ได้ไหม ขอบคุณ
François Romain

4
เซสชันจะได้รับการจัดการที่ฝั่งไคลเอ็นต์และไม่ใช่ฝั่งเซิร์ฟเวอร์ไคลเอ็นต์จะบันทึกโทเค็นและส่งเป็นส่วนหนึ่งของทุกคำขอที่สร้างขึ้น เซิร์ฟเวอร์ตรวจสอบโทเค็นและดำเนินการตามคำขอ
daydreamer

4
ขอคนที่เข้าใจแก้ไขคำตอบนี้ให้พวกเราที่เหลือได้ไหม
Alojz Janez

14

ฉันตอบคำถามที่คล้ายกันที่นี่: AngularJS Authentication + RESTful API


ฉันได้เขียนโมดูล AngularJSสำหรับUserAppที่รองรับเส้นทางที่มีการป้องกัน / สาธารณะการกำหนดเส้นทางใหม่ในการเข้าสู่ระบบ / ออกจากระบบการเต้นของหัวใจสำหรับการตรวจสอบสถานะเก็บโทเค็นเซสชันไว้ในคุกกี้เหตุการณ์ ฯลฯ

คุณสามารถ:

  1. แก้ไขโมดูลและแนบเข้ากับ API ของคุณเองหรือ
  2. ใช้โมดูลร่วมกับUserApp (API การจัดการผู้ใช้บนคลาวด์)

https://github.com/userapp-io/userapp-angular

หากคุณใช้ UserApp คุณจะไม่ต้องเขียนโค้ดฝั่งเซิร์ฟเวอร์สำหรับข้อมูลของผู้ใช้ (มากกว่าการตรวจสอบโทเค็น) เข้าร่วมหลักสูตร Codecademyเพื่อทดลองใช้

นี่คือตัวอย่างบางส่วนของวิธีการทำงาน:

  • วิธีระบุเส้นทางที่ควรเป็นสาธารณะและเส้นทางใดที่เป็นแบบฟอร์มการเข้าสู่ระบบ:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    $routeProvider.when('/home', {templateUrl: 'partials/home.html'});

    .otherwise()เส้นทางควรตั้งค่าที่คุณต้องการให้ผู้ใช้ของคุณจะเปลี่ยนเส้นทางหลังจากเข้าสู่ระบบ ตัวอย่าง:

    $routeProvider.otherwise({redirectTo: '/home'});

  • แบบฟอร์มเข้าสู่ระบบพร้อมการจัดการข้อผิดพลาด:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
  • แบบฟอร์มลงทะเบียนพร้อมการจัดการข้อผิดพลาด:

    <form ua-signup ua-error="error-msg">
      <input name="first_name" placeholder="Your name"><br>
      <input name="login" ua-is-email placeholder="Email"><br>
      <input name="password" placeholder="Password" type="password"><br>
      <button type="submit">Create account</button>
      <p id="error-msg"></p>
    </form>
  • ลิงค์ออกจากระบบ:

    <a href="#" ua-logout>Log Out</a>

    (สิ้นสุดเซสชันและเปลี่ยนเส้นทางไปยังเส้นทางการเข้าสู่ระบบ)

  • เข้าถึงคุณสมบัติของผู้ใช้:

    คุณสมบัติของผู้ใช้สามารถเข้าถึงได้โดยใช้userบริการเช่น:user.current.email

    หรือในเทมเพลต: <span>{{ user.email }}</span>

  • ซ่อนองค์ประกอบที่ควรมองเห็นได้เมื่อเข้าสู่ระบบเท่านั้น:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

  • แสดงองค์ประกอบตามสิทธิ์:

    <div ua-has-permission="admin">You are an admin</div>

และในการตรวจสอบสิทธิ์กับบริการแบ็คเอนด์ของคุณเพียงแค่ใช้user.token()เพื่อรับโทเค็นเซสชันและส่งพร้อมกับคำขอ AJAX ที่ส่วนหลังให้ใช้UserApp API (หากคุณใช้ UserApp) เพื่อตรวจสอบว่าโทเค็นถูกต้องหรือไม่

หากคุณต้องการความช่วยเหลือโปรดแจ้งให้เราทราบ!


ฉันจะ"แก้ไขโมดูลและแนบเข้ากับ API ของคุณเอง" ได้อย่างไร
Pureferret

2

ใน angularjs คุณสามารถสร้างส่วน UI บริการ Directives และทุกส่วนของ angularjs ซึ่งแสดงถึง UI เป็นเทคโนโลยีที่ดีในการทำงาน

ในฐานะที่เป็นคนใหม่ในเทคโนโลยีนี้และต้องการตรวจสอบ "ผู้ใช้" ฉันขอแนะนำให้ทำด้วยพลังของ c # web api สำหรับสิ่งนั้นคุณสามารถใช้ข้อกำหนด OAuth ซึ่งจะช่วยให้คุณสร้างกลไกการรักษาความปลอดภัยที่แข็งแกร่งเพื่อพิสูจน์ตัวตนผู้ใช้ เมื่อคุณสร้าง WebApi ด้วย OAuth คุณจะต้องเรียก api นั้นสำหรับโทเค็น:

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

และเมื่อคุณได้รับโทเค็นแล้วคุณจะขอทรัพยากรจาก angularjs ด้วยความช่วยเหลือของ Token และเข้าถึงทรัพยากรที่รักษาความปลอดภัยใน web Api ด้วยข้อกำหนด OAuth

โปรดอ่านบทความด้านล่างเพื่อรับความช่วยเหลือเพิ่มเติม: -

http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/


1

ฉันคิดว่าการตอบกลับ JSON ทุกรายการควรมีคุณสมบัติ (เช่น {authenticated: false}) และไคลเอ็นต์ต้องทดสอบทุกครั้ง: ถ้าเป็นเท็จตัวควบคุม / บริการเชิงมุมจะ "เปลี่ยนเส้นทาง" ไปยังหน้าการเข้าสู่ระบบ

แล้วจะเกิดอะไรขึ้นถ้าผู้ใช้จับ de JSON แล้วเปลี่ยนบูลเป็น True?

ฉันคิดว่าคุณไม่ควรพึ่งพาฝั่งลูกค้าในการทำสิ่งเหล่านี้ หากผู้ใช้ไม่ได้รับการพิสูจน์ตัวตนเซิร์ฟเวอร์ควรเปลี่ยนเส้นทางไปยังหน้าล็อกอิน / ข้อผิดพลาด


2
ตรวจสอบสิ่งนี้: github.com/witoldsz/angular-http-auth - interceptor จะตรวจสอบรหัสสถานะการตอบกลับของเซิร์ฟเวอร์และถ้าเป็น 403 ('ต้องเข้าสู่ระบบ') จะถ่ายทอดเหตุการณ์เพื่อให้คุณสามารถจับได้ภายในแอพและแสดงช่องล็อกอิน
aherok

10
เลิกตอบกันโดยใช้คำตอบ นั่นคือสิ่งที่แสดงความคิดเห็น!
Soviut

ข้อเสนอแนะของ @aherok ความคิดเห็นของคุณควรได้รับการส่งเสริมให้เป็นคำตอบซึ่งจะได้รับการโหวตให้อยู่ในอันดับต้น ๆ ส่วนที่เหลือเป็นเพียงเสียงรบกวน
user237419

0

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.