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 { ... } |
Active Directory Authentication in ASP.NET MVC 5 with Forms Authentication and Group-Based Authorization的更多相关文章
- Forms Authentication in ASP.NET MVC 4
原文:Forms Authentication in ASP.NET MVC 4 Contents: Introduction Implement a custom membership provid ...
- [转]Implementing User Authentication in ASP.NET MVC 6
本文转自:http://www.dotnetcurry.com/aspnet-mvc/1229/user-authentication-aspnet-mvc-6-identity In this ar ...
- Asp.Net MVC 身份验证-Forms
Asp.Net MVC 身份验证-Forms 在MVC中对于需要登录才可以访问的页面,只需要在对应的Controller或Action上添加特性[Authorize]就可以限制非登录用户访问该页面.那 ...
- 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 bee ...
- 简化 Web 应用程序与 Windows Azure Active Directory、ASP.NET 和 Visual Studio 的集成
大家好! 今天的博文深入讨论我们今天推出的开发人员工具和框架中的一些新功能.我们通过与 ASP.NET 和 Visual Studio 团队合作开发了一些重大的增强功能,让开发人员能够轻松使用 Win ...
- Winbind authentication against active directory
Winbind authentication against active directory Description This tip will describe how to configure ...
- ASP.NET MVC 随想录——探索ASP.NET Identity 身份验证和基于角色的授权,中级篇
在前一篇文章中,我介绍了ASP.NET Identity 基本API的运用并创建了若干用户账号.那么在本篇文章中,我将继续ASP.NET Identity 之旅,向您展示如何运用ASP.NET Ide ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇
在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇(转)
ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇 阅读目录 ASP.NET Identity 前世今生 建立 ASP.NET Identity 使用ASP.NET ...
随机推荐
- Luogu2586 [ZJOI2008]杀蚂蚁 ---- 模拟
Luogu2586 [ZJOI2008]杀蚂蚁 题意 还是一道大模拟 https://www.luogu.org/problemnew/show/P2586 大概就是炮塔大蚂蚁的故事 下载这个游戏ht ...
- ssm中从页面到controller和数据库出现乱码问题的解决
1.确保项目编码为utf8,点击项目右键,点击properties 2.确保数据库编码为utf8,以MySQL为例,可到mysql目录下,my.ini文件中修改后,重启mysql服务 重启mysql服 ...
- poj很好很有层次感(转)
OJ上的一些水题(可用来练手和增加自信) (POJ 3299,POJ 2159,POJ 2739,POJ 1083,POJ 2262,POJ 1503,POJ 3006,POJ 2255,POJ 30 ...
- cocos2d-x项目创建和打包
1.创建C++的cocos2d-x项目:cocos new test_cpp02 -p com.benmutou.helloWorld -l cpp -d projects C++未编译目录: C++ ...
- 《es6标准入门》chapter11中关于Proxy的一个错误例子的纠正
在原书第二版的p120,这里有一个使用Proxy实现管道化调用的例子,想法很好,但是代码有问题,下面是更正之后的代码. 由于我是在node环境下运行,所以我把几个全局函数定义到global内了,如果是 ...
- Jmeter远程测试
11.3 详解JMeter远程测试(1) 2012-04-09 09:14 温素剑 电子工业出版社 字号:T | T 综合评级: 想读(7) 在读(2) 已读(0) 品书斋鉴(0) 已有9 ...
- Reg 命令修改注册表
首先要说明:编辑注册表不当可能会严重损坏您的系统.在更改注册表之前,应备份计算机上任何有价值的数据 只有在别无选择的情况下,才直接编辑注册表.注册表编辑器会忽略标准的安全措施,从而使得这些设置会降低性 ...
- JAVA JSON解析:类XPATH解析JSON
目前JAVA解析JSON的方式有很多种,json-lib啊,GJSON啊,等等都可以解析,但通常是将JSON转换为对象或者是LIST或者是MAP,对于我们测试人员来说,其实我们并不需要里面的全部信息, ...
- C++中 线程函数为静态函数 及 类成员函数作为回调函数
线程函数为静态函数: 线程控制函数和是不是静态函数没关系,静态函数是在构造中分配的地址空间,只有在析构时才释放也就是全局的东西,不管线程是否运行,静态函数的地址是不变的,并不在线程堆栈中static只 ...
- Java中使用FileputStream导致中文乱码问题的修改方案
package com.pocketdigi; import java.io.File; import java.io.FileInputStream; import java.io.FileOutp ...