Windows イントラネットの ServiceStack カスタム認証
私は一日中これに頭を悩ませており、次のことを思いつきました。
最初のユースケース:
Windows 認証を使用して企業イントラネットを使用しています。web.config で authentication mode="Windows" を設定すれば完了です。
あなたの戦略は次のとおりです。
ユーザーのテーブルや ActiveDirectory グループなどに含まれていないため、ユーザーが誰であるかはわかりません。この場合、「ゲスト」の役割を与え、それに応じて UI をトリムします。アクセスをリクエストするための電子メール リンクを提供することもできます。
ユーザーのリストにユーザーがいますが、役割が割り当てられていません。そのため、「ユーザー」の役割を与えて、上記のように UI をトリムします。たぶん彼らは自分のものを見ることができますが、他には何もありません.
ユーザーはリストにあり、役割が割り当てられています。最初に、データベースの UserAuth テーブルを手動で更新してロールを割り当てます。最終的には、許可されたユーザーに対してこれを行うサービスができます。
それでは、コードに行きましょう。
サーバ側
ServiceStack サービス レイヤーでは、 https://github.com/ServiceStack/ServiceStack/wiki/Authentication-and-authorizationに従ってカスタム資格情報認証プロバイダーを作成します。
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
//NOTE: We always authenticate because we are always a Windows user!
// Yeah, it's an intranet
return true;
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
// Here is why we set windows authentication in web.config
var userName = HttpContext.Current.User.Identity.Name;
// Strip off the domain
userName = userName.Split('\\')[1].ToLower();
// Now we call our custom method to figure out what to do with this user
var userAuth = SetUserAuth(userName);
// Patch up our session with what we decided
session.UserName = userName;
session.Roles = userAuth.Roles;
// And save the session so that it will be cached by ServiceStack
authService.SaveSession(session, SessionExpiry);
}
}
カスタムメソッドは次のとおりです。
private UserAuth SetUserAuth(string userName)
{
// NOTE: We need a link to the database table containing our user details
string connStr = ConfigurationManager.ConnectionStrings["YOURCONNSTRNAME"].ConnectionString;
var connectionFactory = new OrmLiteConnectionFactory(connStr, SqlServerDialect.Provider);
// Create an Auth Repository
var userRep = new OrmLiteAuthRepository(connectionFactory);
// Password not required.
const string password = "NotRequired";
// Do we already have the user? IE In our Auth Repository
UserAuth userAuth = userRep.GetUserAuthByUserName(userName);
if (userAuth == null ){ //then we don't have them}
// If we don't then give them the role of guest
userAuth.Roles.Clear();
userAuth.Roles.Add("guest")
// NOTE: we are only allowing a single role here
// If we do then give them the role of user
// If they are one of our team then our administrator have already given them a role via the setRoles removeRoles api in ServiceStack
...
// Now we re-authenticate out user
// NB We need userAuthEx to avoid clobbering our userAuth with the out param
// Don't you just hate out params?
// And we re-authenticate our reconstructed user
UserAuth userAuthEx;
var isAuth = userRep.TryAuthenticate(userName, password, out userAuthEx);
return userAuth;
}
appHost 構成で、関数の最後に次の ResponseFilters を追加します。
ResponseFilters.Add((request, response, arg3) => response.AddHeader("X-Role",request.GetSession(false).Roles[0]));
ResponseFilters.Add((request, response, arg3) => response.AddHeader("X-AccountName", request.GetSession(false).UserName));
これにより、いくつかの追加ヘッダーがクライアントに送信され、ユーザーの役割に従って UI をトリミングできるようになります。
クライアント側
クライアント側では、サーバーに最初のリクエストを送信するときに、カスタム認証で必要な UserName と Password を POST します。HttpContext.Current.User.Identity.Name を介してサーバー側でユーザーが誰であるかがわかるため、両方とも「NotRequired」に設定されています。
以下では、AJAX 通信に AngularJS を使用しています。
app.run(function($templateCache, $http, $rootScope) {
// Authenticate and get X-Role and X-AccountName from the response headers and put it in $rootScope.role
// RemeberMe=true means that the session will be cached
var data={"UserName" : "NotRequired", "Password" : "NotRequired", "RememberMe": true };
$http({ method : 'POST', url : '/json/reply/Auth', data : data }).
success(function (data, status, headers, config) {
// We stash this in $rootScope for later use!
$rootScope.role = headers('X-Role');
$rootScope.accountName = headers('X-AccountName');
console.log($rootScope.role);
console.log($rootScope.role);
}).
error(function (data, status, headers, config) {
// NB we should never get here because we always authenticate
toastr.error('Not Authenticated\n' + status, 'Error');
});
};