使用临时会话时,它工作正常。登录 auth 服务并在不带任何参数的情况下调用 /auth,它会显示显示名称、会话 ID 等。
当我使用 RememberMe=true 登录时,该调用会正确返回会话信息。但是在随后不带任何参数调用 /auth 时,ServiceStack 返回 401 not authenticated。会话对象的 IsAuthenticated 属性为 true 并且实际存在。我的代码对此进行检查,如果它为假,则将用户转发到不会发生的登录页面,因此我知道用户确实已通过身份验证。
我没有做任何不同的事情。如何使用永久会话进行身份验证并获得对 /auth 的后续调用以确认我已登录?
如果有帮助,我正在使用 CustomCredentialsProvider。
更新:
应用主机代码:
public override void Configure(Funq.Container container)
{
//Set JSON web services to return idiomatic JSON camelCase properties
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
Config.RestrictAllCookiesToDomain = ConfigurationManager.AppSettings["cookieDomain"];
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomCredentialsProvider()
{ SessionExpiry =
TimeSpan.FromMinutes(Convert.ToDouble(ConfigurationManager.AppSettings["SessionTimeout"]))
},
}) //end IAuthProvider
{
IncludeAssignRoleServices = false,
IncludeRegistrationService = false,
HtmlRedirect = ConfigurationManager.AppSettings["mainSiteLink"] + "Login.aspx"
} //end AuthFeature initializers
);//end plugins.add AuthFeature
Plugins.Add(new PostmanFeature() { EnableSessionExport = true });// this is only for when we want the feature and it's NOT in DebugMode
Plugins.Add(new SwaggerFeature());
Plugins.Add(new CorsFeature(allowedOrigins: "*",
allowedMethods: "GET, POST, PUT, DELETE, OPTIONS",
allowedHeaders: "Content-Type, Authorization, Accept",
allowCredentials: true));
container.Register<IRedisClientsManager>
(c => new PooledRedisClientManager(2, ConfigurationManager.AppSettings["redisIpPort"]));
container.Register<ICacheClient>(c => c.Resolve<IRedisClientsManager>().GetCacheClient());
container.Register<ISessionFactory>(c => new SessionFactory(c.Resolve<ICacheClient>()));
var userRep = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRep);
//Set MVC to use the same Funq IOC as ServiceStack
ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
#if DEBUG
Config.DebugMode = true;
typeof(Authenticate).AddAttributes
(
new RestrictAttribute
(RequestAttributes.HttpGet | RequestAttributes.HttpPost)
);
#else
typeof(Authenticate).AddAttributes(new RestrictAttribute(RequestAttributes.HttpPost));
#endif
RegisterTypedRequestFilter<Authenticate>((req, res, dto) =>
{
if (dto.UserName != null && dto.UserName != string.Empty
&& dto.Password != null && dto.Password != string.Empty)
if(dto.RememberMe == null)
dto.RememberMe = false;
});
RegisterTypedResponseFilter<AuthenticateResponse>((req, res, dto) =>
{
var appSettings = new ServiceStack.Configuration.AppSettings();
dto.UserId = AppHostBase.Instance.TryResolve<ICacheClient>().SessionAs<CustomUserSession>().UserId.ToString();
dto.Meta = new Dictionary<string, string>();
dto.Meta.Add("ExpiresMinutes", appSettings.Get("SessionTimeout"));
});
}
public static void Start()
{
Licensing.RegisterLicense(licenceKey);
new ServiceStackAppHost().Init();
}
初始请求标头:
https://****.com/api2/auth?username=user&password=passwordmberme=true
- GET /api2/auth?username=user&password=password&rememberme=true HTTP/1.1
- 接受:文本/html,应用程序/xhtml+xml,/
- 接受语言:en-US
- 用户代理:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) 像 Gecko
- 接受编码:gzip,放气
- 主办方:propel.zola360.com
- DNT: 1
- 连接:保持活动
- 饼干:ss-pid=P2hslABCmSs7pomRqNz5;ss-opt=烫发;X-UAId=
初始响应标头:
- HTTP/1.1 200 正常
- 缓存控制:私有
- 内容类型:文本/html
- 内容编码:gzip
- 变化:接受编码
- 服务器:Microsoft-IIS/7.5
- X-Powered-By:ServiceStack/4.033 Win32NT/.NET
- 访问控制允许来源:*
- 访问控制允许方法:GET、POST、PUT、DELETE、OPTIONS
- Access-Control-Allow-Headers:内容类型、授权、接受
- 访问控制允许凭据:true
- X-AspNet-版本:4.0.30319
- 设置 Cookie:ss-id=pojZkNAdMcEcACDREcRM;域=.zola360.com;路径=/; HttpOnly
- 设置 Cookie:ss-opt=perm;域=.zola360.com;过期=格林威治标准时间 2034 年 11 月 13 日星期一 16:11:09;- 路径=/;HttpOnly
- 设置 Cookie:X-UAId=; 域=.zola360.com;过期=格林威治标准时间 2034 年 11 月 13 日星期一 16:11:09;路径=/; HttpOnly
- 设置 Cookie:47=0;域=.zola360.com;路径=/
- 设置 Cookie:用户 ID=47;域=.zola360.com;路径=/
- X-Powered-By: ASP.NET
- 日期:格林威治标准时间 2014 年 11 月 13 日星期四 16:11:09
- 内容长度:4129
初始响应正文:
{"userId":"47","sessionId":"PKrITmRawxAtnaABCDgN","userName":"user","responseStatus":{},"meta":{"ExpiresMinutes":"360"}}
随后调用 /auth 请求:
- 获取 /api2/auth HTTP/1.1
- 接受:文本/html,应用程序/xhtml+xml,/
- 接受语言:en-US
- 用户代理:Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) 像 Gecko
- 接受编码:gzip,放气
- 主办方:propel.zola360.com
- DNT: 1
- 连接:保持活动
- 饼干:ss-pid=cvgslABCmSs6pomYdLu0;ss-opt=烫发;X-UAId=; ss-id=lYWZkFAdMcZcABCDcRM; 47=0;用户 ID=47
随后调用 /auth 响应
- HTTP/1.1 401 未认证
- 缓存控制:私有
- 内容类型:文本/html
- 变化:接受
- 服务器:Microsoft-IIS/7.5
- X-Powered-By:ServiceStack/4.033 Win32NT/.NET
- 访问控制允许来源:*
- 访问控制允许方法:GET、POST、PUT、DELETE、OPTIONS
- Access-Control-Allow-Headers:内容类型、授权、接受
- 访问控制允许凭据:true
- X-AspNet-版本:4.0.30319
- X-Powered-By: ASP.NET
- 日期:格林威治标准时间 2014 年 11 月 13 日星期四 16:11:23
- 内容长度:9731
随后调用 /auth 正文:
{"responseStatus":{"errorCode":"Not Authenticated","message":"Not Authenticated","stackTrace":"[Authenticate: 11/13/2014 3:27:49 PM]:\n[REQUEST: {}]\nServiceStack.HttpError: Not Authenticated\r\n at ServiceStack.Auth.AuthenticateService.Post(Authenticate request)\r\n at lambda_method(Closure , Object , Object )\r\n at ServiceStack.Host.ServiceRunner` 1.Execute(IRequest request, Object instance, TRequest requestDto)","errors":[]}}
更新 我制作了一个小的 Python3 脚本来验证自己并调用其他一些 Web 服务。在使用 RememberMe=true 进行身份验证后,cookie 按预期返回:ss-id/pid 设置正确且 ss-opt=perm。我想我会打印标头 cookie,然后将其粘贴到另一个请求的标头中,以调用标有 [Authenticate] 的不同服务。它没有用。所以我尝试了一些愚蠢的方法并将 ss-pid cookie 值粘贴到 ss-id 中。有效。
这是失败的 cookie 字符串(会话已编辑 :)):
cookie = " ss-id=ss-ID-session-cookie ; domain=.zola360.com; path=/; HttpOnly, ss- pid=ss-PID-session-cookie ; domain=.zola360.com; expires=Tue ,2034 年 11 月 14 日 01:34:25 GMT;路径=/;HttpOnly,ss-opt=perm;域=.zola360.com;过期=周二,2034 年 11 月 14 日 01:34:25 GMT;路径= /; HttpOnly, X-UAId=; domain=.zola360.com; expires=Tue, 14-Nov-2034 01:34:25 GMT; path=/; HttpOnly, 47=0; domain=.zola360.com; path =/,用户 ID=47;域=.zola360.com;路径=/"
只需将 ss-pid 值粘贴到 ss-id 中即可:
cookie = " ss-id=ss-PID-session-cookie ; domain=.zola360.com; path=/; HttpOnly, ss- pid=ss-PID-session-cookie ; domain=.zola360.com; expires=Tue ,2034 年 11 月 14 日 01:34:25 GMT;路径=/;HttpOnly,ss-opt=perm;域=.zola360.com;过期=周二,2034 年 11 月 14 日 01:34:25 GMT;路径= /; HttpOnly, X-UAId=; domain=.zola360.com; expires=Tue, 14-Nov-2034 01:34:25 GMT; path=/; HttpOnly, 47=0; domain=.zola360.com; path =/,用户 ID=47;域=.zola360.com;路径=/"
还有我使用的 Python3 脚本:
import httplib2 as http
import json
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json; charset=UTF-8'
}
uri = 'https://mysite.com'
path = '/api2/auth/credentials'
target = urlparse(uri+path)
method = 'POST'
body = '{"username": "username", "password": "password", "RememberMe": "true"}'.encode()
h = http.Http()
response, content = h.request(target.geturl(), method, body, headers)
#save the cookie and use it for subsequent requests
cookie = response['set-cookie']
print(cookie)
path2 = '/api2/time/start'
target2 = urlparse(uri+path2)
headers['cookie'] = cookie
response, content = h.request(target2.geturl(), 'GET', body, headers)
# assume that content is a json reply
# parse content with the json module
data = json.loads(content.decode())
print(data)
即使 ss-opt=perm,似乎仍有一些东西在看 ss-id 的值。