如何实现适配新Security API的ASP.NET MVC AuthorizeAttribute?
Problem Statement
I need to integrate a new Security API into multiple existing ASP.NET MVC applications in our organization. These apps originally use Windows authentication, with security enforced via .NET's AuthorizeAttribute like this:
[Authorize(Roles="MY_CORP\Group1,MY_CORP\Group2")] public class MyClass { // }
Now I need to update this implementation to use the new Security API, which fetches users and their groups with this code:
var user = new SecurityApi().GetUser(userId); var groups = user.Groups;
I want to keep the same attribute-based syntax, using roles returned by the Security API (e.g., GroupX, GroupY):
[Authorize(Roles="GroupX, GroupY")] public class MyClass { // }
How can I achieve this?
Solution: Create a Custom AuthorizeAttribute
Great question! The core idea is to override the default AuthorizeAttribute logic to use your new Security API instead of relying on Windows authentication's built-in role checks. Here's a complete, step-by-step implementation:
1. Build the Custom Authorization Attribute
Create a new class that inherits from AuthorizeAttribute and override the AuthorizeCore method—this is where we'll plug in the Security API integration:
using System; using System.Linq; using System.Web.Mvc; public class SecurityApiAuthorizeAttribute : AuthorizeAttribute { protected override bool AuthorizeCore(HttpContextBase httpContext) { // First, verify the user is authenticated (adjust this if your auth flow changed) if (!httpContext.User.Identity.IsAuthenticated) { return false; } // Get the current user's ID (use your app's method to retrieve the authenticated user's identifier) string userId = httpContext.User.Identity.Name; try { // Fetch user data and their groups from the new Security API var securityApi = new SecurityApi(); var user = securityApi.GetUser(userId); var userGroups = user.Groups; // Parse the required roles specified in the attribute (trim whitespace for cleanliness) var requiredRoles = Roles.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(role => role.Trim()) .ToList(); // Check if the user has at least one of the required roles return requiredRoles.Any(role => userGroups.Contains(role)); } catch (Exception ex) { // Log the API error (use your organization's logging system) // Example: Logger.Error("Security API call failed", ex); // Fail closed: deny access if the API is unavailable return false; } } // Optional: Customize what happens when authorization fails protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { // Redirect to a custom access denied page (adjust the path to match your app) filterContext.Result = new RedirectResult("/Account/AccessDenied"); // For API controllers, you might prefer a 403 response instead: // filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Forbidden); } }
2. Update Your Code to Use the Custom Attribute
Replace the default [Authorize] attribute with your new custom one. You can keep the exact same Roles syntax you wanted:
[SecurityApiAuthorize(Roles="GroupX, GroupY")] public class MyClass { // Your class implementation here }
3. Key Optimizations & Considerations
- Caching: To avoid hitting the Security API on every request (which can hurt performance), add caching for user/group data. For example, cache the groups for a user in the session or a distributed cache like Redis.
- User ID Retrieval: If you've moved away from Windows authentication, adjust how you get
userId—this might come from a JWT token claim, session variable, or another authentication mechanism. - Role Matching: Ensure the role names from the Security API exactly match what you specify in the
Rolesparameter (case sensitivity matters unless you adjust the comparison logic).
内容的提问来源于stack exchange,提问作者user9393635

