如何为单个用户创建登录会话计时器

本文关键字:登录 会话 计时器 创建 用户 单个 | 更新日期: 2023-09-27 17:58:10

我一直在研究我为每个用户的login session创建的custom timer。到目前为止,我无法为每个登录会话进行单独的timers

场景:

User1登录时,timer will start counting .当User2登录时,Use1的计时器将重置该值与User2的计时器相同。似乎他们有one timer(不是个人(。

这就是我想要发生的事情。

  • 当用户 1 登录时,他的计时器将开始计数。
  • 如果计时器达到 900 秒(15 分钟(,它将弹出一些模态框,告知他的会话已超时。
  • 模态将显示至少 30 秒的倒计时
  • 倒计时后,用户将自动注销
  • 每个用户都必须有自己的计时器

我已经完成了所有这些,除了最后一项Every user must have their own timers

这是我关于创建计时器的代码:

public class SessionTimer
{
    private static Timer timer;
    public static void StartTimer()
    {
        timer = new Timer();
        timer.Interval = (double)Utility.ActivityTimerInterval();
        timer.Elapsed += (s, e) => MonitorElapsedTime();
        timer.Start();
    }
    public static void ResetTimer()
    {
        TimeCount = 0;
        timer.Stop();
        timer.Start();
    }
    public static int TimeCount { get; set; }
    public static string ConnectionID { get; set; }
    private static void MonitorElapsedTime()
    {
        if (TimeCount >= Utility.TimerValue())
        {
            timer.Stop();
            Hubs.Notifier.SessionTimeOut(TimeCount);
        }
        else
        {
            Hubs.Notifier.SendElapsedTime(TimeCount);
        }
        TimeCount++;
    }
}  

登录成功后,我将调用计时器开始

[HttpPost]
public ActionResult SignIn(LoginCredentials info)
{
    // Success full login
    SessionTimer.StartTimer();
}  

这是服务器上的信号器代码:

public class SessionTimerHub : Hub
{
    public void SendTimeOutNotice(int time)
    {
        Clients.Client(Context.ConnectionId).alertClient(time);
    }
    public void CheckElapsedTime(int time)
    {
        Clients.Client(Context.ConnectionId).sendElapsedTime(time);
    }
    public void UpdateConnectionID(string id)
    {
        SessionTimer.ConnectionID = id;
    }
}
public class Notifier
{
    public static void SessionTimeOut(int time)
    {
        var context = GlobalHost.ConnectionManager.GetHubContext<SessionTimerHub>();
        context.Clients.Client(SessionTimer.ConnectionID).alertClient(time);
    }
    public static void SendElapsedTime(int time)
    {
        var context = GlobalHost.ConnectionManager.GetHubContext<SessionTimerHub>();
        context.Clients.Client(SessionTimer.ConnectionID).sendElapsedTime(time);
    }
}  

和jquery代码:

$(function () {
        /////////////////////////////////////////////////// SESSION TIMER
        var timer = $.connection.sessionTimerHub, $modaltimer = $('#session_timer_elapsed'), tt = null;
        timer.client.alertClient = function (time) {
            var $count = $modaltimer.find('.timer'), wait = 180;
            $count.text(wait);
            $modaltimer.modal('show');
            tt = setInterval(function () {
                $count.text(wait--);
                if (wait < 0) {
                    $.post('@Url.Action("Logout", "Auth")', function () { window.location.reload(); });
                    window.clearInterval(tt);
                }
            }, 1000);
        };
        timer.client.sendElapsedTime = function (time) {
            console.log(time);
        };
        $.connection.hub.start().done(function () {
            timer.server.updateConnectionID($.connection.hub.id);
        });
        $modaltimer.on('click', '.still_here', function () {
            $.post('@Url.Action("ResetTimer", "Auth")');
            $modaltimer.modal('hide');
            window.clearInterval(tt);
        }).on('click', '.log_out', function () {
            $.post('@Url.Action("Logout", "Auth")', function () { window.location.reload(); });
            $modaltimer.modal('hide');
        });
});  

如您所见,我正在这样做:

timer.server.updateConnectionID($.connection.hub.id);  

传递连接 ID,因为我无法在 public class Notifier 中获取 ID

我失败的解决方案

我尝试使用dynamicExpandoObject
SessionTimer放入session例如:

public static dynamic Data
{
    get
    {
        #region FAILSAFE
        if (HttpContext.Current.Session[datakey] == null)
        {
            HttpContext.Current.Session[datakey] = new ExpandoObject();
        }
        #endregion
        return (ExpandoObject)HttpContext.Current.Session[datakey];
    }
}  

它成功地分离了计时器。但是在我的扩展对象变量
上传递连接 id 时例如:

public void UpdateConnectionID(string id)
{
    MyExpandoObject.MySessionTimer.ConnectionID = id;
} 

它引发空引用异常。从 SignalR 传递数据时,我的 expandoObject 似乎变得为空(只是我的想法(,但我不确定。

请帮助我使用这个单独的计时器,并在计时器过后向特定用户发送消息。


请注意我想在服务器端创建计时器。

从服务器重置计时器

计时器必须能够在服务器上重置。在这种情况下,我将自定义属性放在每个AcrionReseult

例如:

[HttpPost]
[BasecampAuthorize]
public ActionResult LoadEmailType()
{
    return Json(Enum.GetNames(typeof(EmailType)).ToList());
}  

当用户通过[BasecampAuthorize]时,这意味着他进行了活动。
[BasecampAuthorize]

public class BasecampAuthorizeAttribute : AuthorizeAttribute
{
    string url { get; set; }
    public BasecampAuthorizeAttribute()
    {
        if (string.IsNullOrEmpty(url))
        {
            url = "~/SomeUrl";
        }
    }
    public BasecampAuthorizeAttribute(string URL)
    {
        url = URL;
    }
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.HttpContext.Response.Redirect(url);
        }
        else
        {
            // MUST RESET SESSION TIMER HERE
        }
        base.OnAuthorization(filterContext);
    }
}  

@halter73 - 如何在此处调用重置计时器?

如何为单个用户创建登录会话计时器

您的问题是您只有一个静态计时器变量,该变量是在整个 AppDomain 中共享的签名实例。在 ASP.NET 中,AppDomain是每个Web应用程序而不是每个用户。可以使用静态变量,但该变量应是一个集合,其中包含每个 connectionId 的唯一计时器。如果在负载平衡器后面横向扩展或 IIS 重新启动应用程序,这显然会创建新的 AppDomain,这将崩溃。

public class SessionTimer : IDisposable
{
    public static readonly ConcurrentDictionary<string, SessionTimer> Timers;
    private readonly Timer timer;
    static SessionTimer()
    {
        Timers = new ConcurrentDictionary<string, SessionTimer>();
    }
    private SessionTimer(string connectionID)
    {
        ConnectionID = connectionID;
        timer = new Timer();
        timer.Interval = (double)Utility.ActivityTimerInterval();
        timer.Elapsed += (s, e) => MonitorElapsedTime();
        timer.Start();
    }
    private int TimeCount { get; set; }
    private string ConnectionID { get; set; }
    public static void StartTimer(string connectionID)
    {
        var newTimer = new SessionTimer(connectionID);
        if (!Timers.TryAdd(connectionID, newTimer))
        {
            newTimer.Dispose();
        }
    }
    public static void StopTimer(string connectionID)
    {
        SessionTimer oldTimer;
        if (Timers.TryRemove(connectionID, out oldTimer))
        {
            oldTimer.Dispose();
        }
    }
    public void ResetTimer()
    {
        TimeCount = 0;
        timer.Stop();
        timer.Start();
    }
    public override Dispose()
    {
        // Stop might not be necessary since we call Dispose
        timer.Stop();
        timer.Dispose();
    }
    private void MonitorElapsedTime()
    {
        if (TimeCount >= Utility.TimerValue())
        {
            StopTimer(ConnectionID);
            Hubs.Notifier.SessionTimeOut(ConnectionID, TimeCount);
        }
        else
        {
            Hubs.Notifier.SendElapsedTime(ConnectionID, TimeCount);
        }
        TimeCount++;
    }
}

由于将连接 ID 存储在 SessionTimer 类中,因此在调用 Notifier 类中的方法时,只需将其作为参数传入即可。

public static class Notifier
{
    private static context = GlobalHost.ConnectionManager.GetHubContext<SessionTimerHub>(); 
    public static void SessionTimeOut(string connectionID, int time)
    {
        context.Clients.Client(connectionID).alertClient(time);
    }
    public static void SendElapsedTime(string connectionID, int time)
    {
        context.Clients.Client(connectionID).sendElapsedTime(time);
    }
}

您不需要SendTimeOutNoticeCheckElapsedTime,因为您要在 Notifier 类中调用客户端方法。 UpdateConnectionID可以替换为OnConnected

public class SessionTimerHub : Hub
{
    public override Task OnConnected()
    {
        SessionTimer.StartTimer(Context.ConnectionId);
        return base.OnConnected();
    }
    public override Task OnDisconnected()
    {
        SessionTimer.StopTimer(Context.ConnectionId);
        return base.OnDisconnected();
    }
    public void ResetTimer()
    {
        SessionTimer.Timers[Context.ConnectionId].ResetTimer();
    }
}

应在中心内创建SessionTimers,以便将其与连接 ID 关联。可以在OnConnected中执行此操作,但这意味着只有在登录后才应启动 SignalR 连接,并且你实际上想要启动该连接的SessionTimer。它还有助于在中心上安装ResetTimer,以便您拥有客户端的连接 ID。或者,可以从客户端上获取连接 ID $.connection.hub.id并发布它。

$.connection.hub.start().done(function () {
    $modaltimer.on('click', '.still_here', function () {
        timer.server.resetTimer();
        $.post('@Url.Action("ResetTimer", "Auth")');
        $modaltimer.modal('hide');
        window.clearInterval(tt);
    });
});

编辑:

如果由于某种原因SessionTimerHub.ResetTimer抛出KeyNotFoundException(如果您在火灾后呼叫timer.server.resetTimer,则不应该发生这种情况$.connection.hub.start().done(,您可以执行以下操作:

public void ResetTimer()
{
    SessionTimer timer;
    if (SessionTimer.Timers.TryGetValue(Context.ConnectionId, out timer))
    {
        timer.ResetTimer();
    }
    else
    {   
        SessionTimer.StartTimer(Context.ConnectionId);
    }
}

如果由于某种原因 IIS 重新启动应用程序,您可能希望在SessionTimerHub.OnReconnected中添加此内容,因为客户端将重新连接,但静态 SessionTimer.Timers 将被重置,并且所有 SessionTimers 都将消失。

public override Task OnReconnected()
{
    if (!SessionTimer.Timers.ContainsKey(Context.ConnectionId))
    {
        SessionTimer.StartTimer(Context.ConnectionId);
    }
    return base.OnReconnected();
}

你从来没有用 C# 调用过SessionTimerHub.ResetTimer()吧?

已经有一个世界的计时器:用户的时钟。让用户登录时自己的时间倒计时:将他的个人登录时间保存在javascript cookie中。

假设我们正在使用 RSA 密钥 ( openssl genrsa 2048 ( 对用户进行身份验证。

现在的对话是:

> GET /login
< 200 OK
challenge: mie4NaNgfcqwkhYtNiy4oF
<form action='/login'>
  <input type='text' name='user-public-key' />
  <input type='text' name='rsa-signed-challenge' />
</form>
> POST /login
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA24PnkIZYg/k6lLbNZl5X
IxNl8KEsJTUX7h9B2P+o5LjB6e1OozVwsPFwXjCJP3WN8KwBQhvpxH4ZCK9H0LyT
+NXBPCsydBgY2VfqKK3wQONRKUaEQkSlV/gA0ciWa/jXD9FDbc3onrlqNTmtjGiM
OkXzSJDPz67tEDx+sZQ/+zcEO9tVPSioq6tZwMHx3EfBratA9W148OZRLOS1AFmc
RSSJzsgj/MmOTxrGjfNE2dKMvul4usjWf8Of+GEpEqW6+SrKZ3ivhP/jx+q1v8Bp
pbhDYag6I+YTwEu4feXJEgM4z83e05DJvh3hUDgzv7KDMqCp/h3m8f3TolcgvNox
fQIDAQAB
-----END PUBLIC KEY-----
rsa-signed-challenge:
IyGoX2aqQOH/tBRxxxGPIWGmbo2r6KfEmcDUdDvt7nNaN0GwqXm6MZaJ6eQvjkCR
AVnVRpJNGQDWCaLqzyBLVrysTlnC0dHkJD/vJg3jv74UqZGOqeKSW06HIhzz79UY
RghkdjadDpA8Jzs8NSYBzFopExfzSz+K1sOOwVXWa9nywhGqEj7XXoJO1I0j+o63
Wt94xEa30gmW1oVWIvjWLnBewH4H9ZzXv8PeGTdLdp2v9c9a3nsd7PsYi2yHul+S
CfAlFo/hITfEqucUX5zgyJyU0+SAVRod+vRlSaimMW2CQq8K8kQSbADaQpET4pa1
9eVnG99rz2UHw9b6UG0nBQ==
< 301 Redirect /dashboard
< Set-Cookie: Token=
iPtyfAxAcenFog1kn/h5VrdkBj0wxjeZBnB4JfXLcOWlh+G6/GdndgT7VTg3rSyq
uNFfbgnKNQtiGBjJ45cUHDai44ILK0g6DXPcicusEhs30xJhh8CT4P2FfK/juTtz
d0nj9ypncFOsUPAzJdirgdxO9oMquw0DW2b/iG1O/Dn2fU1/lDrmFpKKIMnPZ10g
cD6gY7Yhqs+2OcyfuiuIBYf2tAq8LYtKSm69j0AlgEiFNNmPl12Wr1R7YhxJZ1hi
smDjNTom5tDfxi3LDfwFtsHKn61OCbfuI3PhlPPqYTUd6omL1Efbr29l4jj+9cgF
0NnaX1L6LUUd6RaVmCw30w==

> GET /dashboard
> Cookie: Token=
iPtyfAxAcenFog1kn/h5VrdkBj0wxjeZBnB4JfXLcOWlh+G6/GdndgT7VTg3rSyq
uNFfbgnKNQtiGBjJ45cUHDai44ILK0g6DXPcicusEhs30xJhh8CT4P2FfK/juTtz
d0nj9ypncFOsUPAzJdirgdxO9oMquw0DW2b/iG1O/Dn2fU1/lDrmFpKKIMnPZ10g
cD6gY7Yhqs+2OcyfuiuIBYf2tAq8LYtKSm69j0AlgEiFNNmPl12Wr1R7YhxJZ1hi
smDjNTom5tDfxi3LDfwFtsHKn61OCbfuI3PhlPPqYTUd6omL1Efbr29l4jj+9cgF
0NnaX1L6LUUd6RaVmCw30w==
< 200 OK
<html>
<head>
  <script>
    jQuery.fn.startCountDown = function(el, opts) {
      opts = $.extend({ minutes : 20 }, opts);
      var start = new Time();
      var updater = function() { el.text("Seconds left: " + (opts.minutes * 60 - ((new Time()) - start))); }
      setTimeout(1000, updater);
    }
    $(function() {
      $("#timer").startCountDown({ minutes: 30 });
    })
  </script>
</head>
<body><p id="timer"></p></body>
</html>

这里的关键是你不能相信客户端提供倒计时的合理值,所以上面的解决方案加密登录时间并将其用作令牌。

这是从客户的角度出发的。

$ openssl genrsa 2048 >client.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA24PnkIZYg/k6lLbNZl5XIxNl8KEsJTUX7h9B2P+o5LjB6e1O
ozVwsPFwXjCJP3WN8KwBQhvpxH4ZCK9H0LyT+NXBPCsydBgY2VfqKK3wQONRKUaE
QkSlV/gA0ciWa/jXD9FDbc3onrlqNTmtjGiMOkXzSJDPz67tEDx+sZQ/+zcEO9tV
PSioq6tZwMHx3EfBratA9W148OZRLOS1AFmcRSSJzsgj/MmOTxrGjfNE2dKMvul4
usjWf8Of+GEpEqW6+SrKZ3ivhP/jx+q1v8BppbhDYag6I+YTwEu4feXJEgM4z83e
05DJvh3hUDgzv7KDMqCp/h3m8f3TolcgvNoxfQIDAQABAoIBAQCg8VrsObPYPvjW
ZBjAf1a/3s8U1/Z36S98ZOpwYTHBUDzMeDL5sorHEJ3kUQ2vu06wMExT3gdNC27r
USgEQN70yDP/G2TIfYpqf+ysmqrVyFSPQKZjt9TKZIilRr4St8VmUXVwolF1Xlgi
YgF+OoDlkLfIcnQKvyQMjW4OYLVwRw+bCKAq/T45kki+X41VK4Ubsnjddy+yT++3
GKMPqezVmZHGuhyVtR+dB9vQB3zWZocQRrqDDoQviDB7+scQD2XeWz53SUScBdwm
TzW5YaYclbttVWib0okCSxnhF3yah8cqvQHgqalrACnhaQx9oLcqyrg6KLCI/Lnn
yOZLvKZ5AoGBAP46Y8waGAYgPtgI/7Rni4NslnANGh6psWKaELftPLThxiuCoHad
rMpSojqP8FbKgqILDUOwczPIy/+jWK6J8EJPv4za2dlOcGnnfzD3Ko6LN2Jq3reC
N0Ywi3esTaesHp/SDOB39xW3XAAkdsejffy+LHsnrpVKYKlVm43Jhy6nAoGBAN0L
k+0m9RgwOxWiiAJK3XSBgdHK0e9BnVMKFVPiS2Q1wrnOUDb1CansAAyYSVZYsJZD
eh5VocYPGPTydl/muywlPceGgW2O1CO/LZPj1cKQANHx0+6F9itmk8leaTR4rTuf
2G67sdvlYCyKPoIvVg3WJQnj3dsqo4Ldt/5KkSc7AoGBAMDbIqHOmbLr+0B/cxs0
AY3tbiIKjmn8aOhX357nhUnijCatrXTOICpLjW3Hi5cLgRXUNHfI/1ulU7vV+oxN
b8meHb2IuAI1kumEB+TpW4tO6PDsCZBEZBIG+YYLW816sLCk88fEudfrhQtGniTM
TeLRkYTLkZEHH1TV8G8bFkW5AoGAXf9XZ2jCnwebiIa2KatmYu3To8AI6CJR4YcP
LL21a6bE6LiIOeaXtm+KUdDMlvBeH3gQTSgDBDNVXIxitENs4sfvbpKPJWSwZ4cb
vaEMPJF6F80rX2oOFcSoIeCJAmwy1oERy3z7lFQFQsuC619vy7B9zafdpx6Jq9PX
M0bIVRMCgYEAy9LvPbyXMWYmOy9svYRy4iAL9sCRNmzZgvC5B5b3lN55EWoE8ipw
g4qNBv3NaCJ/lTFezRQZVRfFfQOpNGDvJBLmTEaSR3OuV8hRw7zttC3/MazDqwTy
qZeg526uklXN7IvkFfiHlYZeed1u4wc7SXSi76RIE0w5lDBIb+CPPM4=
-----END RSA PRIVATE KEY-----
$ openssl rsa -in client.key -pubout >client.key.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA24PnkIZYg/k6lLbNZl5X
IxNl8KEsJTUX7h9B2P+o5LjB6e1OozVwsPFwXjCJP3WN8KwBQhvpxH4ZCK9H0LyT
+NXBPCsydBgY2VfqKK3wQONRKUaEQkSlV/gA0ciWa/jXD9FDbc3onrlqNTmtjGiM
OkXzSJDPz67tEDx+sZQ/+zcEO9tVPSioq6tZwMHx3EfBratA9W148OZRLOS1AFmc
RSSJzsgj/MmOTxrGjfNE2dKMvul4usjWf8Of+GEpEqW6+SrKZ3ivhP/jx+q1v8Bp
pbhDYag6I+YTwEu4feXJEgM4z83e05DJvh3hUDgzv7KDMqCp/h3m8f3TolcgvNox
fQIDAQAB
-----END PUBLIC KEY-----

这是我们从服务器获得的挑战(随机垃圾(

$ echo "mie4NaNgfcqwkhYtNiy4oF" >to-sign
$ openssl dgst -sha1 -sign client.key -out to-sign.sha1 to-sign

(可选:确保客户端的签名有效(

$ openssl dgst -sha1 -verify client.key.pub -signature to-sign.sha1 to-sign

现在,您可以以可复制的方式对客户端签名进行编码:

$ cat to-sign.sha1 | openssl base64 -e
g8nbi3mrcZ8afEpf4iRG6TQFDJtxw48AJ0QTKOdTKh2khxbQdPQIxAFWU++xsJRd
m0wnKRc1SpEYxhLeMKYjyhMTDce/KUBM/5i1Hoh9RPi9+G/cxNoXJiVKaxIF+rDf
NYk7mQD9ofdYgBCAbu2hi6jR5t2BY6emd7z/F53E8edVFtzqlHYjVLHNNYiNlXqD
WHl9OLV2b+yHY/mAkgUBYoVjyBvFnHFcRxTrZMC9A8K3jfU/bEOLgxEmq3UgmvWC
2M7fwpOBAUN7q9yoIR/kOGNPqePghhHTuyeFtbC33PmD8qgbgVUQby8jpVQ7T+5Q
kjEP2sbnETHlkjS6TXSKHg==

现在让我们对身份验证令牌进行签名,以便我们转到服务器...

$ openssl genrsa 2048 >server.key
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAoD80ZhPHm1x62wAESbEdHmzCdYCwC3VwkOwDMr4fD9Qqmw6D
Zu2oMSAJ8Kw1Q4oRJd3sZkHu7ZnCwHTe3P83uBKrCfDDFiu/aB1mJQJDAZzPwh5r
W8/2FAHmq/iw/f3U3IOaPuE1w7eoEaqFuNnmfb0EEBhsi7zhbjKpKowUXObPz1W5
mIJvpWqlyygHG6+JJVMs1jTdwzzQzcyHZDgEu/fODHgW5ErBQS7fKOOvZZIxEuLg
i7x5xB/mMYpWdIO0BI/xcu3SdmmNE/Ix8MoAZXXTK9u8Uu+2jnO69pFWtBGPIa2l
AGKNQ1jlUgvECglo356OOpKx9rb9Fd9WY4SIxwIDAQABAoIBABIiFNPYOSYjeON/
RPzxxdHDjN2vCjzBtVMw4cvEJ8+quoeBRO1Ix1eHwJgzZHOYFAis7CtGGrtYQul0
UCPB3ZQ+yIv/apP/r1EgwoY9k0eDbx8QQiXJipcJAAlFwwF6z7OEUNf8tBDJn4Mg
QLGCNsrTsLoBiYbmgLvvj6T45PT+G3ztaETv7iiiUIMX5R/7thK/+odiUeqyIbHH
m0673m0nYdZvGe7ujZarbh1h2x4Srs+OiMaPXH1ehw/nTvYAoEgGvz8eGVFCUrfa
87rizYMLYk8zs/TAz4CDtAk9PtLVMS6NZdKCYe4zvLBdjGjwx7gQJFpjXJCRQwA2
JGE8U8ECgYEAzn4I5yHc+ApzJDJyRmeR70MMWk0KpzIi+8PhwKUNpdP3DyKGOTAs
Z3g3su1/C5tcq2GIK/hprvDxdtg7wu7RSZdhz1W3Ee584JTqzqAG464bmCqd1aPi
sp7DVOeSrb5EZDtrZ9UpW79R2skRhU3qLAay0bl/cvKauBukCr0gUUsCgYEAxqq+
slkXI1tvCzaoxAWNyf0hooGKjEeAsCyrFYJwVk9cveJ0wMZUC7pLPH9UVEIjZzzo
Fm+xr5kVbXhLBn9cCXjP/OgBtuM1KqA/mcsmysVn2okuU8BIy43RYXgq1+eUI92/
MDFKtuFzMikSqI5gY3sIB0GRfL19FAvdy4iKtPUCgYEApUiJA8k9QGXM6Epg4i4A
yA1ZE+bbAh3FltSiHTuAgx35genWmmwO/vthSh2ENdwz/xJglyGOJnPCM6i9nTjf
2RINPpKTqQzGdFV+5cl9+jzg5ZonIFzAFs2x+IIsDFpiEADn5gLfygqIEKIlHhjR
uk/aTrk2ZOIAKiIl2lqsRaUCgYA3K8O5k7QxRXsZChzkEwbFSV7F2mO3gUPjqQP5
/TdlQLToprL1th4xA5NRQasRmyxpxyhM0sftk/23YOi07TmKB9r6yRNwzrg9FjOT
ai9jsF6e+em7qHKO1NuIze5X9x/UtggaQhYVo5ZyH6Xm2WM7PTeFjFfy5EyP/Juj
ok+i4QKBgQCf6mndYOkBCmzyMv2gASr8nj7Fh0oTxfaFRrs9k/DnUTOSUgrHrmO4
eCAk/D5FdPjcmF1np8wasVt38sw5nxUmbYonoV/2H+xmvKrRqtuflGRQx98P/+Qd
6vIF/n3NM66oZG9zgeYdEzxAbLXptCO60arJ4Ekrod/J+EpGSQb+bg==

以下是服务器在设置时运行的内容:

$ openssl rsa -in server.key -pubout >server.key.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoD80ZhPHm1x62wAESbEd
HmzCdYCwC3VwkOwDMr4fD9Qqmw6DZu2oMSAJ8Kw1Q4oRJd3sZkHu7ZnCwHTe3P83
uBKrCfDDFiu/aB1mJQJDAZzPwh5rW8/2FAHmq/iw/f3U3IOaPuE1w7eoEaqFuNnm
fb0EEBhsi7zhbjKpKowUXObPz1W5mIJvpWqlyygHG6+JJVMs1jTdwzzQzcyHZDgE
u/fODHgW5ErBQS7fKOOvZZIxEuLgi7x5xB/mMYpWdIO0BI/xcu3SdmmNE/Ix8MoA
ZXXTK9u8Uu+2jnO69pFWtBGPIa2lAGKNQ1jlUgvECglo356OOpKx9rb9Fd9WY4SI
xwIDAQAB
-----END PUBLIC KEY-----

现在服务器有了私钥和公钥,让我们创建令牌已签名并用于身份验证。

$ echo "2013-02-09T16:33:44+0000" >auth.token.plain

由于要加密的文本非常小,因此我们不会遇到密钥问题。服务器上的长度,因此我们可以按原样使用密钥,而不是这样做对称加密。

$ openssl rsautl -encrypt -inkey server.key.pub -pubin -in auth.token.plain -out auth.token.cipher

这是我们给客户的。每次我们收到此令牌时,我们都可以验证我们签了字。如果您有服务器场,请让他们使用相同的密钥进行签名。

$ cat auth.token.cipher | openssl base64 -e
iPtyfAxAcenFog1kn/h5VrdkBj0wxjeZBnB4JfXLcOWlh+G6/GdndgT7VTg3rSyq
uNFfbgnKNQtiGBjJ45cUHDai44ILK0g6DXPcicusEhs30xJhh8CT4P2FfK/juTtz
d0nj9ypncFOsUPAzJdirgdxO9oMquw0DW2b/iG1O/Dn2fU1/lDrmFpKKIMnPZ10g
cD6gY7Yhqs+2OcyfuiuIBYf2tAq8LYtKSm69j0AlgEiFNNmPl12Wr1R7YhxJZ1hi
smDjNTom5tDfxi3LDfwFtsHKn61OCbfuI3PhlPPqYTUd6omL1Efbr29l4jj+9cgF
0NnaX1L6LUUd6RaVmCw30w==

服务器可以再次验证令牌。解密密码失败意味着它已被修改,令牌失败。生产系统也应该验证签名(在同一信封中(以避免数据解密损坏成功。

$ openssl rsautl -decrypt -inkey server.key -in auth.token.cipher
2013-02-09T16:33:44+0000

在母版页中:

public partial class MasterPage : System.Web.UI.MasterPage
{
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Session["LoginID"] != null)
            Label1.Text="Welcome :: " + Session["LoginID"].ToString();
        else
            Response.Redirect("Login.aspx");
    }
    protected void lnkLogout_Click(object sender, EventArgs e)
    {
        Session["LoginID"] = null;
        Response.Redirect("Login.aspx");
    }
}

在 CS 页面中:

protected void Login_Click(object sender, EventArgs e)
    {
        string conString = "Provider=Microsoft.JET.OLEDB.4.0; data source=" + Server.MapPath (string.Empty)  + @"'Database'Northwind.mdb";
        string sqlString = "SELECT * FROM CUSTOMERS where CustomerID='" + TextBox1.Text + "' and City='" + TextBox2.Text + "'";
        OleDbConnection conn = new OleDbConnection(conString);
        DataSet ds = new DataSet();
        OleDbDataAdapter adapter = new OleDbDataAdapter(sqlString, conn);
        adapter.Fill(ds);
        if (ds != null)
        {
            if (ds.Tables[0].Rows.Count > 0)
            {
                Session["LoginID"] = ds.Tables[0].Rows[0]["CustomerID"].ToString();
                Response.Redirect("Welcome.aspx");
            }
            else
                Label1.Text = "Enter correct id/city";
        }

    }
}