服务堆栈 - 一条路线上的自定义验证

时间:2017-02-13 22:23:44

标签: authentication servicestack jwt

在我目前的应用程序中,为了安全起见,我正在使用Service Stack with JWT' s。安全性已经实施并且运行良好。麻烦的是,我想确保一条路线与其他路线不同。登录用户检索的文档,我想确保他们检索的文档是他们的,而不是其他人。这是非常敏感的数据。我想以不同的方式保护它,因为像PostMan这样的东西可以与有效的令牌一起使用来检索任何文档,我想阻止它。用户ID在令牌中,我希望将其与正在检索的文档匹配(如果可能)。当前的安全性实现如下:

public class AppHost: AppHostBase
{

    public override void Configure(Funq.Container container)
    {
        Plugins.Add(new AuthFeature(() => new AuthUserSession(),
           new IAuthProvider[] {
            new JsonWebTokenAuthProvider("myKey", "myAudience"),
        }));
    }
}

JsonWebTokenAuthProvider是一个实现安全性的自定义类,这一切都很完美。这是代码:

public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
    {
        // first validate the token, then get roles from session
        string header = request.oauth_token;

        // if no auth header, 401
        if (string.IsNullOrEmpty(header))
        {
            throw HttpError.Unauthorized(MissingAuthHeader);
        }

        string[] headerData = header.Split(' ');

        // if header is missing bearer portion, 401
        if (!string.Equals(headerData[0], "BEARER", StringComparison.OrdinalIgnoreCase))
        {
            throw HttpError.Unauthorized(InvalidAuthHeader);
        }

        // swap - and _ with their Base64 string equivalents
        string secret = SymmetricKey.Replace('-', '+').Replace('_', '/');
        string token = headerData[1].Replace("\"", "");
        // set current principal to the validated token principal

        Thread.CurrentPrincipal = JsonWebToken.ValidateToken(token, secret, Audience, true, Issuer);
        string lanId = GetLanID(Thread.CurrentPrincipal.Identity.Name);
        string proxyAsLanId = request.Meta.ContainsKey(META_PROXYID) ? request.Meta[META_PROXYID] : null;

        if (HttpContext.Current != null)
        {
            // set the current request's user the the decoded principal
            HttpContext.Current.User = Thread.CurrentPrincipal;
        }

        // set the session's username to the logged in user
        session.UserName = Thread.CurrentPrincipal.Identity.Name;
        session.Roles = GetApplicableRoles(lanId, proxyAsLanId);

        authService.Request.SetItem("lanID", lanId);
        authService.Request.SetItem("proxyAsLanId", proxyAsLanId);

        return OnAuthenticated(authService, session, null, null);
    }

我查找RequestFilterAttribute找到了here,但我认为这不是我想要的。理想情况下,如果检查失败,我想尽可能返回401(未经授权)。

这样做的最佳方式是什么?

1 个答案:

答案 0 :(得分:2)

如果您只想以不同的方式处理一条路线,只需在单一服务中添加验证,例如:

public object Any(MyRequest dto)
{
    var lanId = base.Request.GetItem("lanId");
    if (!MyIsValid(lanId))
        throw HttpError.Unauthorized("Custom Auth Validation failed");   
}

您可以在RequestFilter中执行相同的操作,例如:

public class CustomAuthValidationAttribute : RequestFilterAttribute
{
    public override void Execute(IRequest req, IResponse res, object responseDto)
    {
        var lanId = req.GetItem("lanId");
        if (!MyIsValid(lanId))
        {
            res.StatusCode = (int) HttpStatusCode.Unauthorized;
            res.StatusDescription = "Custom Auth Validation failed";
            res.EndRequest();
        }
    }
}

并将其应用于单个服务:

[CustomAuthValidation]
public object Any(MyRequest dto)
{
    //...
}

或服务的集合,例如:

[CustomAuthValidation]
public class MyAuthServices : Service
{
    public object Any(MyRequest1 dto)
    {
        //...
    }
    public object Any(MyRequest2 dto)
    {
        //...
    }
}