센로그
13. Authorization 본문
◆ Authorization
authorization은 권한과 관련된 문제이다.
인증을 통과한 사용자라고 하더라도 사용자마다 허용된 권한은 다를 수 있다.
ASP.NET Core에서 권한 관련 작업은 필터를 통해 처리된다.
요청 처리 과정 속에서 보면, 다음 시점(4.1)에서 처리된다.
- Request
- Routing
- Authentication 미들웨어 (인증)
- MVC 미들웨어
- Authorize 필터 적용
- Action
- View
Authorization시 사용하는 애트리뷰트는 다음과 같다.
- [Authorize]
- 권한이 있는 경우에만 해당 액션 또는 컨트롤러를 사용 가능하도록 함
- [AllowAnonymous]
- 필터 특성상, 적용 범위를 세부적으로 설정할 수 있다. (Global, Controller, Action 등)
- [Authorize] 필터가 붙은 컨트롤러 내부에서, 예외적으로 권한 없이도 접근할 수 있는 액션을 설정하고 싶을 때 사용
Authorization Fail
그러면 권한이 없으면 어떻게 될까?
권한이 없는 경우, MVC에서는 다음과 같은 IActionResult를 생성하고, 이에 따라 특정 View를 보여준다.
- ChallengeResult
- 로그인하지 않은 상태
- ForbidResult
- 로그인은 했는데, 권한은 없는 상태
WebAPI의 경우, 다음과 같은 Status Code를 반환한다.
- 401
- 403
Authorization Policy
권한에 관한 정책(Policy)도 설정할 수 있다.
Authorization Policy
- Request가 Authorize(권한 부여)되기 위해 필요한 정책
- [Authorize("정책이름")]을 통해 정책 사용 선언 가능
그럼 정책은 어떻게 만들까?
Program.cs에다 등록해주면 된다.
builder.Services.AddAuthorization(options =>
{
//정책 등록
});
정책을 만드는 몇가지 예제 코드를 살펴보자.
builder.Services.AddAuthorization(options =>
{
//정책: IsAdmin이라는 클레임이 있어야 한다. (DB의 AspNetUserClaims)
options.AddPolicy("AdminPolicy", policy => policy.RequireClaim("IsAdmin"));
//정책: Email 주소가 "grace7040@naver.com"이어야 한다.
options.AddPolicy("TestPolicy", policy => policy.RequireClaim(ClaimTypes.Email, "grace7040@naver.com"));
//정책: 인증받은 유저여야 한다. (기본값이긴 함)
options.AddPolicy("TestPolicy", policy => policy.RequireAuthenticatedUser());
//여러가지를 복합적으로 체크하고 싶을 때 사용. p는 authorization handler context
//정책: Email 타입의 클레임을 가지고 있어야 한다.
options.AddPolicy("TestPolicy", policy => policy.RequireAssertion(
p => p.User.HasClaim(c => c.Type == ClaimTypes.Email)));
});
- 만드는 방법이나 기능은 되게 많으니, 필요할 때 구글링해서 사용 ㄱㄱ
1. Program.cs에 정책 등록
builder.Services.AddAuthorization(options =>
{
//정책: IsAdmin이라는 클레임이 있어야 한다. (DB의 AspNetUserClaims)
options.AddPolicy("AdminPolicy", policy => policy.RequireClaim("IsAdmin"));
});
2. 클레임 추가
AdminPolicy라는 정책을 만드려면, 실제로 DB에 IsAdmin이라는 클레임을 추가해줘야 할 것이다.
- Register.cshtml.cs에서 using System.Security.Claims; 를 하고, 새 클레임 추가
//Test
var claim = new Claim("IsAdmin", Input.Email.StartsWith("admin").ToString());
await _userManager.AddClaimAsync(user, claim); //DB에 클레임 추가
- 이메일이 admin으로 시작하는 경우 IsAdmin 클레임을 주도록 설정했다.
3. 권한을 필요로하는 컨트롤러나 액션에다 Authorize 애트리뷰트를 붙여준다.
- [Authorize("AdminPolicy")]
테스트를 위해 HomeController의 Privacy 액션에다 AdminPolicy를 추가해줬다.
[Authorize("AdminPolicy")]
public IActionResult Privacy()
{
return View();
}
4. 이제 실행을 해보자.
- Privacy를 누르면, 권한이 필요한 페이지이므로 우선 로그인하라고 뜬다.
- grace7040@khu.ac.kr 계정으로 로그인 하면, 아직 따로 설정해준 권한이 없기 때문에 Dennied 된다.

그럼 아까 IsAdmin 클레임을 주는 조건이었던, admin으로 시작하는 계정을 만들어보자.
- adminSeeyoun@naver.com 이라는 계정을 등록하고, 로그인한다.
- 이 경우 Privacy 페이지에 접근할 수 있는 것을 확인할 수 있다.
실제로 AspNetUserClaims에 들어가보면, 다음과 같이 IsAdmin권한이 True인 것을 확인할 수 있다.

- 5~8번까지가 adminSeeyoun@naver.com에 대한 클레임 정보임. (UserId로 식별)
CustomPolicy Class
앞에서는 AddAuthorization을 통해 하나씩 AddPolicy를 했었다.
그러나 정책이 복잡해지고 많아질수록 이 방법은 보기 힘들어질 것임
따라서, 단순하지 않은 복잡한 정책에 대해서는 CustomPolicy를 클래스로 만들어 관리할 수 있다.
예를들어 클럽 입장 정책을 구성한다고 생각해보자.
다음과 같은 정책에 따라 입장 권한을 부여한다고 하자.
- 20~30세 or VIP
- 블랙리스트에 없어야 함 (!IsBanned)
이 클럽 입장 정책을 CustomPolicy 클래스로 구현해보자.
우선 알아둬야할 개념:
Policy는 쉽게 말해 Requirement(AND) + Handler(OR) 이다.
- Policy는 하나 이상의 Requirement로 구성
- Requirement는 하나 이상의 Handler로 구성
이 개념을 기반으로, 클럽 입장 정책은 다음과 같이 구현할 수 있다
- [CanEnterRequirement]
- AgeHandler : 20-30세인지 판단
- IsVipHandler : VIP여부 판단
- [IsNotBlackListRequirement]
- IsUnbannedHandler : IsBanned == false인지 판단
위 명세를 실제로 구현해보자.
1. 우선 Policy 를 위한 Requirement 클래스 및 Handler 클래스를 만들고, 연결해주자.
public class CanEnterRequirement : IAuthorizationRequirement
{
public int MinAge { get; }
public int MaxAge { get; }
public CanEnterRequirement(int minAge, int maxAge)
{
MinAge = minAge;
MaxAge = maxAge;
}
}
//Ok, Pass, Fail 중 하나를 반환함.
//순서대로 통과, 중립, 실패 를 의미함
public class AgeHandler : AuthorizationHandler<CanEnterRequirement> //핸들러와 관련된 requirement
{
//구현해야 하는 메서드
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CanEnterRequirement requirement)
{
//Age타입을 추출해서 클레임을 가져옴
Claim claim = context.User.Claims.FirstOrDefault(c => c.Type == "Age");
if(claim != null)
{
int age = int.Parse(claim.Value);
//MinAge <= age <= MaxAge이면 Succeed
if(requirement.MinAge <= age && requirement.MaxAge >= age)
{
context.Succeed(requirement); //Ok
}
}
//Requirement가 만족되지 않았으면 아무것도 안함. (만족된 경우 Succeed()에서 성공 플래그 꽂음)
return Task.CompletedTask;
}
}
public class IsVipHandler : AuthorizationHandler<CanEnterRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CanEnterRequirement requirement)
{
Claim claim = context.User.Claims.FirstOrDefault(c => c.Type == "IsVip");
if (claim != null)
{
bool vip = bool.Parse(claim.Value);
if (vip)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
public class IsNotBlackListRequirement : IAuthorizationRequirement
{
}
public class IsUnbannedHandler : AuthorizationHandler<IsNotBlackListRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, IsNotBlackListRequirement requirement)
{
Claim claim = context.User.Claims.FirstOrDefault(c => c.Type == "IsBanned");
if (claim != null)
{
bool ban = bool.Parse(claim.Value);
if (ban)
{
context.Fail(); //Fail : 다른 거 볼 것도 없이 실패
}
else
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}
2. 그리고 Program.cs에다 Policy 등록 및 DI 등록을 해준다.
builder.Services.AddAuthorization(options =>
{
//정책 추가
options.AddPolicy("ClubEnterPolicy", policy =>
{
//Requirement 추가
policy.AddRequirements(
new CanEnterRequirement(20, 30),
new IsNotBlackListRequirement()
);
});
});
//DI 추가
builder.Services.AddSingleton<IAuthorizationHandler, AgeHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, IsVipHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, IsUnbannedHandler>();
- ClubEnterPolicy 라는 이름의 정책을 추가해줌
- 두가지 Requirement를 추가해줌
- 각종 핸들러를 DI 해줌
3. 마지막으로 해당 정책을 사용할 컨트롤러나 액션에다 [Authorize("정책이름")]을 붙여주면 된다.
[Authorize("ClubEnterPolicy")]
public IActionResult Privacy()
{
return View();
}
4. 여러 계정을 사용한 실행 결과는 다음과 같다.
- 우측 상단에 있는 계정 명을 보자. 나이/vip여부/(밴여부) 로 구성했다.





Authorization 수동 처리
마지막으로 , Authorization 수동 처리에 대해서 알아보자.
지금까지는 필터를 사용해서 아예 거부하거나, 아예 통과시키거나 두개 중 하나로 처리를 했었다.
그러지 않고, Action 내부에서 조건에 따라 유동적으로 특정 처리를 하고 싶다면?
- IAuthorizationService 이용하면 됨
다음과 같이 작성하면 앞의 예제와 동일하게 동작한다.
public class HomeController : Controller
{
private IAuthorizationService _auth;
public HomeController(IAuthorizationService auth)
{
_auth = auth;
}
public async Task<IActionResult> Privacy()
{
var result = await _auth.AuthorizeAsync(User, "ClubEnterPolicy");
if(!result.Succeeded) {
return new ForbidResult();
}
return View();
}
}
'게임 > ASP.NET Core' 카테고리의 다른 글
15. Custom Middleware (0) | 2024.04.15 |
---|---|
14. Logging (0) | 2024.04.15 |
12. Authentication (0) | 2024.04.15 |
11. Filter (0) | 2024.04.15 |
10. Configuration (0) | 2024.04.15 |