问题
In my MVC 5 application, I decorate my controllers as follows:
[Authorize]
public class Controller
{
..
However, one requirement I have is to use a token to authorize an action without going to the login screen. ie: http://{website}/Action?token={/* token for this user */}
Thus, how can I develop a custom AuthorizeAttribute that accepts a login (default behavior) OR a token (custom, required behavior)?
In other words, if I use http://{website}/Action, I would be redirected to the login screen (if I am not authorized), but if I use http://{website}/Action?token={/* token for this user */}, I would be authorized and redirected to said action.
[TokenAuthorize] class
public class TokenAuthorize : AuthorizeAttribute
{
private const string SecureToken = "token";
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Authorize(filterContext))
{
return;
}
HandleUnauthorizedRequest(filterContext);
}
private bool Authorize(AuthorizationContext actionContext)
{
try
{
HttpRequestBase request = actionContext.RequestContext.HttpContext.Request;
string token = request.Params[SecureToken];
return SecurityManager.IsTokenValid(token);
}
catch (Exception)
{
return false;
}
}
}
If I decorate my controllers with:
[Authorize]
[TokenAuthorize]
public class Controller
{
..
It is processed as Authorize AND TokenAuthorize(1). I need to develop a way to process such as Authorize OR TokenAuthorize
回答1:
How about decorating with only TokenAuthorize and then falling back to the default behavior if no token exists?
TokenAuthorize.cs
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
bool isTokenAuthorized = HasValidToken(...);
if(isTokenAuthorized) return true;
bool isDefaultAuthorized = base.AuthorizeCore(httpContext);
if(isDefaultAuthorized) return true;
return false;
}
MyController.cs
[TokenAuthorize]
public class MyController
{
...
}
回答2:
Shoe's answer led me on the right track.
I implemented his suggestion and did the following in my Authorize function:
private bool Authorize(AuthorizationContext actionContext)
{
try
{
HttpContextBase context = actionContext.RequestContext.HttpContext;
string token = context.Request.Params[SecurityToken];
bool isTokenAuthorized = SecurityManager.IsTokenValid(token);
if (isTokenAuthorized) return true;
bool isDefaultAuthorized = AuthorizeCore(context);
return isDefaultAuthorized;
}
catch (Exception)
{
return false;
}
}
Decorating with just [TokenAuthorize], I can authorize an action via login (default) OR via token.
回答3:
Perfect Code: Validate token from DB
public class TokenAuthorize : AuthorizeAttribute
{
string _connectionString;
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Authorize(filterContext))
{
return;
}
HandleUnauthorizedRequest(filterContext);
}
protected bool Authorize(AuthorizationContext httpContext)
{
bool isTokenAuthorized = HasValidToken();
if(isTokenAuthorized) return true;
return false;
}
protected bool HasValidToken()
{
string token = string.Empty;
token = HttpContext.Current.Request.Params["token"];
_connectionString = WebConfigurationManager.ConnectionStrings["SqlConnectionString"].ConnectionString;
SqlTransaction txn = null;
using (SqlConnection conn = new SqlConnection(_connectionString))
{
conn.Open();
txn = conn.BeginTransaction();
List<SqlParameter> parameters = new List<SqlParameter>();
SqlParameter parameter = new SqlParameter();
parameters.Add(new SqlParameter("@token", token));
parameter = new SqlParameter("@return_ops", 0);
parameter.Direction = ParameterDirection.Output;
parameters.Add(parameter);
SqlHelper.ExecuteNonQuery(txn, CommandType.StoredProcedure, "[master_LoggedInUsers]", parameters.ToArray());
int result = Convert.ToInt32(parameters[1].Value);
if (result <= 0)
{
return false;
}
else return true;
}
}
}
[TokenAuthorize] public class MasterController : Controller {}
来源:https://stackoverflow.com/questions/35184229/how-can-i-develop-a-custom-authorizeattribute-that-accepts-a-login-or-a-token