HTTP 1.1에서 규정하고 있는 인증의 종류에는 Basic 인증과 Digest 인증이 있습니다.
http://www.ietf.org/rfc/rfc2617.txt
HTTP 요청에 대해서 인증 처리가 되지 않은 경우, 스테이터스 코드 401을 통해 클라이언트에 리소스 접근에 필요한 인증정보를 통지하게 된다.
[요청]
POST /api/employees/12345 HTTP/1.1
Host: basic.auth.com
[응답]
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="basic.auth.com"
WWW-Authenticate 헤더에 의해서 클라이언트는 서버가 제공하는 인증 방식을 이해할 수 있고, 그 방식에 따른 인증 정보를 보낼 수 있다.
Basic 인증
Basic 인증은 유저 이름과 패스워드로 인증을 합니다. 유저 이름과 패스워드는 Authorization 헤더에 넣어 요청마다 전송한다. Authorization 헤더의 내용은 유저이름:패스워드 로 구성하고 Base64 인코딩한 문자열이 된다. 문자열은 암호처럼 보이지만 실상은 간단히 디코딩이 가능하다.
따라서 네트워크 상에서 암호가 흘러다닌다고 생각할 수 있다. 보다 높은 정도의 보안 강도를 위해서는 SSL와 TLS를 사용해 HTTPS 통신을 사용해야 한다.
다음 그림으로 Basic 인증의 흐름을 볼 수 있다.
아래 코드는 Basic 인증을 처리하기 위한 예제입니다.
[Authorize] 특성을 Controller에 추가해 준다.
public class AuthenticationHandler : DelegatingHandler | |
{ | |
private const string SCHEME = "Basic"; | |
private const string REALM = "basic.auth.com"; | |
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |
{ | |
try | |
{ | |
var headers = request.Headers; | |
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme)) | |
{ | |
Encoding encoding = Encoding.GetEncoding("iso-8859-1"); | |
string credentials = encoding.GetString(Convert.FromBase64String( | |
headers.Authorization.Parameter)); | |
string[] parts = credentials.Split(':'); | |
string userId = parts[0].Trim(); | |
string password = parts[1].Trim(); | |
// TODO - Do authentication of userId and password against your credentials store here | |
if (true) | |
{ | |
var claims = new List<Claim> | |
{ | |
new Claim(ClaimTypes.Name, userId), | |
new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password) | |
}; | |
var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, SCHEME) }); | |
Thread.CurrentPrincipal = principal; | |
if (HttpContext.Current != null) | |
HttpContext.Current.User = principal; | |
} | |
} | |
var response = await base.SendAsync(request, cancellationToken); | |
if (response.StatusCode == HttpStatusCode.Unauthorized) | |
{ | |
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME, "realm=\"" + REALM + "\"")); | |
} | |
return response; | |
} | |
catch (Exception) | |
{ | |
var response = request.CreateResponse(HttpStatusCode.Unauthorized); | |
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME)); | |
return response; | |
} | |
} | |
} |
[Authorize] 특성이 동작하도록 하기 위해서 아래와 같이 구현한 Handler를 추가해 준다.
public static class WebApiConfig | |
{ | |
public static void Register(HttpConfiguration config) | |
{ | |
config.Routes.MapHttpRoute( | |
name: "DefaultApi", | |
routeTemplate: "api/{controller}/{id}", | |
defaults: new { id = RouteParameter.Optional } | |
); | |
// Basic 인증과 관련된 Handler를 추가한다. | |
config.MessageHandlers.Add(new AuthenticationHandler()); | |
} | |
} |
아래 코드는 Basic 인증으로 요청하는 Clinet 예제이다.
AuthenticationHeaderValue를 통해서 헤더값에 인증을 위한 값을 할당하는 것을 볼 수 있다.
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
// Testing Basic Authentication | |
using (HttpClient client = new HttpClient()) | |
{ | |
// Basic 인증을 위한 유저이름:패스워드를 Base64 인코딩해서 헤더에 넣는다. | |
string creds = String.Format("{0}:{1}", "john", "john"); | |
byte[] bytes = Encoding.ASCII.GetBytes(creds); | |
var header = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytes)); | |
client.DefaultRequestHeaders.Authorization = header; | |
var postData = new List<KeyValuePair<string, string>>(); | |
postData.Add(new KeyValuePair<string, string>("Name", "John Q Human")); | |
HttpContent content = new FormUrlEncodedContent(postData); | |
string response = String.Empty; | |
var responseMessage = client.PostAsync("http://basic.auth.com/api/employees/12345", content).Result; | |
if(responseMessage.IsSuccessStatusCode) | |
response = responseMessage.Content.ReadAsStringAsync().Result; | |
Console.WriteLine(response); | |
} | |
} | |
} |
아래 그림은 Fiddler로 요청을 살펴본 내용이다. Authorization으로 헤더에 값이 들어 있는 것을 볼 수 있다.
리소드에 대한 인증처리가 잘 되었을 경우 200 코드.
[참고]
Pro ASP.NET Web API Security
웹 개발자를 위한 웹을 지탱하는 기술
'Language > C#' 카테고리의 다른 글
[ASP.NET MVC] Complie-Time에 View 에러 확인 (0) | 2013.12.20 |
---|---|
HttpClient (0) | 2013.06.29 |
JsonResult Serializer 변경 (0) | 2013.05.10 |
날짜 형식 찾기 위한 정규식 (1) | 2010.04.13 |