Active Directory Authentication in ASP.NET MVC 5 with Forms Authentication and Group-Based Authorization

I know that blog post title is sure a mouth-full, but it describes the whole problem I was trying to solve in a recent project.

The Project

Let me outline the project briefly.  We were building a report dashboard-type site that will live inside the client’s network.  The dashboard gives an overview of various, very important information that relates to how the company is performing on a hourly basis.  So, the dashboard is only available to a certain group of directors.

To limit the solution to the these directors, authentication and authorization would go through their existing Active Directory setup by putting the authorized users in a special AD group.

The Problem

Getting authentication to work was a snap.  Microsoft provides theSystem.Web.Security.ActiveDirectoryMembershipProvider
class to use as your membership provider.  Putting an [Authorize] attribute on my action methods or entire controllers was all I needed to get it working (besides, of course, the system.web/authentication web.config updates and a controller to show my login form and handle the submit credentials).

Here’s my relevant web.config setup:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<connectionStrings>

  <add name="ADConnectionString" connectionString="<ldap connection string here>" />

</connectionStrings>

...

<authentication mode="Forms">

  <forms name=".AuthCookie" loginUrl="~/login"/>

</authentication>

<membership defaultProvider="ADMembershipProvider">

  <providers>

    <clear/>

    <add name="ADMembershipProvider"

         type="System.Web.Security.ActiveDirectoryMembershipProvider"

         connectionStringName="ADConnectionString"

         attributeMapUsername="sAMAccountName"/>

  </providers>

</membership>

The tough part came when I wanted to limit access to users in that AD group. Microsoft doesn’t provide a RoleProvider along with its ActiveDirectoryMembershipProvider. So, what to do?

I tried several methods I found online. Most of them were based on creating my own customRoleProvider and querying AD to iterate through the user’s groups (treating them like roles) and seeing if one of them matched my AD group I was looking for. However, I could never get it to work. Each code example I found eventually gave me this AD error when I iterated through the current user’s AD groups:


1

The specified directory service attribute or value does not exist.

The Solution

Eventually, I found a solution online that worked. Instead of setting up a custom RoleProvider, all it involved was creating a custom AuthorizeAttribute for your MVC controllers (or action methods) that checked the user’s .IsMemberOf method to see if the member belonged the sought after group (or groups). I don’t know why this method does not cause the same AD error as describe above, but I’m glad it doesn’t! All I can assume is that it queries AD in a more friendly way.

Here is my custom AuthorizeAttribute:


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

public class AuthorizeADAttribute : AuthorizeAttribute

{

    private bool _authenticated;

    private bool _authorized;

    public string Groups { get; set; }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)

    {

        base.HandleUnauthorizedRequest(filterContext);

        if (_authenticated && !_authorized)

        {

            filterContext.Result = new RedirectResult("/error/notauthorized");

        }

    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)

    {

        _authenticated = base.AuthorizeCore(httpContext);

        if (_authenticated)

        {

            if (string.IsNullOrEmpty(Groups))

            {

                _authorized = true;

                return _authorized;

            }

            var groups = Groups.Split(‘,‘);

            string username = httpContext.User.Identity.Name;

            try

            {

                _authorized = LDAPHelper.UserIsMemberOfGroups(username, groups);

                return _authorized;

            }

            catch (Exception ex)

            {

                this.Log().Error(() => "Error attempting to authorize user", ex);

                _authorized = false;

                return _authorized;

            }

        }

        _authorized = false;

        return _authorized;

    }

}

Notice that I also included a little code to distinguish between the user not being authenticated (which the call to base.AuthorizeCore takes care of) and not being authorized. Without the code inHandleUnauthorizedRequest, if the user successfully logs in but is not in the AD group, he just sees the log in screen again which doesn’t communicate the problem very well.

The this.Log() code uses a Nuget packaged called this.Log. The LDAPHelper class is something I wrote. The code is below:


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

public static class LDAPHelper

{

    public static string GetLDAPContainer()

    {

        Uri ldapUri;

        ParseLDAPConnectionString(out ldapUri);

        return HttpUtility.UrlDecode(ldapUri.PathAndQuery.TrimStart(‘/‘));

    }

    public static string GetLDAPHost()

    {

        Uri ldapUri;

        ParseLDAPConnectionString(out ldapUri);

        return ldapUri.Host;

    }

    public static bool ParseLDAPConnectionString(out Uri ldapUri)

    {

        string connString = ConfigurationManager.ConnectionStrings["ADConnectionString"].ConnectionString;

        return Uri.TryCreate(connString, UriKind.Absolute, out ldapUri);

    }

    public static bool UserIsMemberOfGroups(string username, string[] groups)

    {

        /* Return true immediately if the authorization is not

        locked down to any particular AD group */

        if (groups == null || groups.Length == 0)

        {

            return true;

        }

        // Verify that the user is in the given AD group (if any)

        using (var context = BuildPrincipalContext())

        {

            var userPrincipal = UserPrincipal.FindByIdentity(context,

                                                 IdentityType.SamAccountName,

                                                 username);

            foreach (var group in groups)

            {

                if (userPrincipal.IsMemberOf(context, IdentityType.Name, group))

                {

                    return true;

                }

            }

        }

        return false;

    }

    public static PrincipalContext BuildPrincipalContext()

    {

        string container = LDAPHelper.GetLDAPContainer();

        return new PrincipalContext(ContextType.Domain, null, container);

    }

}

My code is mostly based on example code I found on a very helpful StackOverflow post:http://stackoverflow.com/questions/4342271/asp-net-mvc-forms-authorization-with-active-directory-groups/4383502#4383502.

To use this code, all you have to do is use your custom AuthorizeAttribute instead of the built-in one. Something like this:


1

2

3

4

5

[AuthorizeAD(Groups="Some AD group name")]

public class HomeController : Controller

{

...

}

时间: 2024-11-07 23:45:39

Active Directory Authentication in ASP.NET MVC 5 with Forms Authentication and Group-Based Authorization的相关文章

Migrating an ASP.NET MVC application to ADFS authentication

I recently built an ASP.NET application at work to help track internal use of our products. It's been really well received, but only a few days after launch one of our managers came over and asked if we could move the site to Azure so that people did

Winbind authentication against active directory

Winbind authentication against active directory Description This tip will describe how to configure authentication settings in CentOS to use authentication against Windows Servers. I will describe how to do it in a command line. The command line argu

ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇(转)

ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇 阅读目录 ASP.NET Identity 前世今生 建立 ASP.NET Identity 使用ASP.NET Identity ASP.NET Identity 其他API介绍 小节 在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP.NET Identity的建

转载 ASP.NET MVC中使用ASP.NET Identity - 新西兰程序员 - 博客园

转载原地址: http://blog.jobbole.com/90695/ 在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP.NET Identity的建立和使用,包括基础类的搭建和用户管理功能的实现-- http://myusermanagement.azurewebsites.net/Account/Login?ReturnUrl=%2F 点此

ASP.NET安全[开发ASP.NET MVC应用程序时值得注意的安全问题](转)

概述 安全在web领域是一个永远都不会过时的话题,今天我们就来看一看一些在开发ASP.NET MVC应用程序时一些值得我们注意的安全问题.本篇主要包括以下几个内容 : 认证 授权 XSS跨站脚本攻击 跨站请求伪造 认证 所谓认证,简单的来说就是验证一个用户的身份.这取决于我们开发的站点的类型,是否允许匿名访问,是否是属于管理员或者其它角色的用户等等.也就是说我们的整个程序或者某些功能是针对某些特定的用户开发的,那么我们可能就要进行认证来确定用户的身份.需要注意的是,认证与授权是是完全不一样的概念

Choosing web framework: ASP.NET MVC vs Django Python vs Ruby on Rails(转载)

来源:http://podlipensky.com/2012/06/choosing-web-framework-asp-net-mvc-vs-django-python-vs-ruby-on-rails/ How often do you emerge from you cubicle to look around, note new faces or new facial expression on old ones? How often do you emerge from you tec

Areas in ASP.NET MVC 4

Download source - 2.7 MB Introduction to Areas In this article, we will learn the concept of Areas and how to use it with a practical scenario in ASP.NET MVC 4. By convention in ASP.NET MVC project template, to separate application code based on resp

ASP.NET MVC 5 Authentication Breakdown

In my previous post, "ASP.NET MVC 5 Authentication Breakdown", I broke down all the parts of the new ASP.NET MVC authentication scheme. That's great, but I didn't have a working example that you, a curious developer, could download and play arou

Enabling Active Directory Authentication for VMWare Server running on Linux《转载》

Enabling Active Directory Authentication for VMWare Server running on Linux Version 0.2 - Adam Breidenbaugh - 2007-06-29 Purpose The purpose of this guide is as follows: Document the steps necessary to enable Active Directory Authentication on a Linu