Как создать таймер сеанса входа в систему для отдельных пользователей

Я работал над этим custom timer, который я создал для каждого пользователя login session. До сих пор мне не удавалось создать индивидуальный timers для каждого сеанса входа в систему.

Сценарий:

Когда User1 входит в систему, timer will start counting. Когда User2 входит в систему, таймер Use1 сбрасывает значение, такое же, как таймер для User2. Вроде у них one timer (не индивидуальный).

Вот чего я хочу.

  • когда user1 входит в систему, его таймер начинает отсчет.
  • если таймер достигнет 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);  

чтобы передать идентификатор подключения, потому что я не могу получить идентификатор внутри public class Notifier

Мои неудачные решения

Я попытался поместить SessionTimer в session, используя dynamic и ExpandoObject
например:

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];
    }
}  

И это удачно разделило таймеры. Но при передаче идентификатора соединения в моей переменной expandoobject
например:

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

он выдает исключение с нулевой ссылкой. Кажется, что мой expandoObject получает значение null при передаче данных из SignalR (просто я думаю), но я не уверен в этом.

Пожалуйста, помогите мне с этими индивидуальными таймерами и отправкой сообщений конкретным пользователям, когда их таймеры истекли.

Обратите внимание
я хочу создать таймер на стороне сервера.

Сброс таймера с сервера

Таймер должен иметь возможность сбрасываться на сервере. В этом случае я добавляю настраиваемые атрибуты на каждый AcrionReseult

e.g.:

[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 - Как здесь вызвать таймер сброса?


person fiberOptics    schedule 07.02.2013    source источник
comment
пожалуйста, укажите причину, по которой вы проголосовали против. Я задаю вопрос, на который мне трудно ответить.   -  person fiberOptics    schedule 07.02.2013
comment
хммм ... может ты ненавидишь мой вопрос. хорошо, в любом случае спасибо.   -  person fiberOptics    schedule 07.02.2013
comment
Я думаю, что причина, по которой люди отклоняют этот вопрос, заключается в том, что вы не создаете программное обеспечение таким образом. Зачем каждому покупателю нужен свой таймер на сервере? Почему они не могут вместо этого получить с сервера подписанный токен, действительный только в течение определенного времени, а затем заставить JavaScript на стороне клиента реализовать таймер?   -  person Henrik    schedule 09.02.2013
comment
@Henrik Спасибо за ваш комментарий, я впервые сталкиваюсь с этой особенностью. Если вы знаете какие-то ссылки, руководства, в которых описывается и обсуждается сказанное вами, поделитесь со мной. Честно говоря, я не знаю, как создать программное обеспечение, отвечающее этим требованиям. Я хочу научиться, подскажите, пожалуйста, что мне делать. Спасибо!   -  person fiberOptics    schedule 09.02.2013
comment
@fiberOptics, я с вами согласен. Я не люблю анонимный снайпер. Я не вижу ничего плохого в вашем вопросе.   -  person Dave Alperovich    schedule 09.02.2013


Ответы (3)


Ваша проблема в том, что у вас есть только одна статическая переменная таймера, которая является экземпляром signle, совместно используемым во всем домене приложений. В ASP.NET домен приложений определяется для веб-приложения, не для пользователя. Вы можете использовать статическую переменную, но эта переменная должна быть коллекцией, содержащей уникальный таймер для каждого connectionId. Это выйдет из строя, если вы выполните горизонтальное масштабирование за балансировщиком нагрузки или IIS перезапустит приложение, что, очевидно, создаст новый домен приложения.

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++;
    }
}

Поскольку вы храните идентификатор соединения внутри класса 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);
    }
}

Вам не нужны SendTimeOutNotice или CheckElapsedTime, поскольку вы вызываете клиентские методы в своем 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 внутри своего концентратора, чтобы вы могли связать его с вашим идентификатором подключения. Вы можете сделать это в OnConnected, как указано выше, но это означает, что вам следует запускать соединение SignalR только после того, как вы вошли в систему, и вы действительно хотите запустить SessionTimer для этого соединения. Также полезно иметь ResetTimer на концентраторе, чтобы у вас был идентификатор подключения клиента. В качестве альтернативы вы можете получить идентификатор подключения на клиенте от $.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();
}

Вы ведь никогда не вызываете SessionTimerHub.ResetTimer() на C #, верно?

person halter73    schedule 08.02.2013
comment
Благословение с небес! Спасибо за ответ, попробовал, работает !!!! Но я забыл указать, что сброс таймера должен выполняться 1) From Clicking the **Yes I'm Here** from client, и 2) From the server.. См. Мою правку. Спасибо! - person fiberOptics; 08.02.2013
comment
возникла проблема при вызове SessionTimer.Timers[Context.ConnectionId].ResetTimer(); выдает ошибку KeyNotFoundException. И когда я проверяю, счетчик SessionTimer.Timers равен 0. Почему это происходит? - person fiberOptics; 08.02.2013
comment
Получение SessionTimer.Timers [Context.ConnectionId] всегда должно работать, если оно вызывается путем вызова timer.server.resetTimer в $.connection.hub.start().done. Вы можете сделать Timers.TryGetValue msdn.microsoft.com/en-us/library/dd267270. aspx и запустите новый SessionTimer, если он не существует. - person halter73; 09.02.2013
comment
Как я могу получить доступ к Context.ConnectionId, если мой код для сброса таймера находится внутри public override void OnAuthorization(AuthorizationContext filterContext)? - person fiberOptics; 09.02.2013
comment
Если вам нужно сбросить таймер в OnAuthorization, я бы просто использовал что-то вроде IIdentitity.Name пользователей в качестве ключа для SessionTimer.Timers, поскольку это доступно в вашем хабе и в OnAuthorization. Внутри вашего SessionTimerHub к нему можно получить доступ как Context.User.Identity.Name. Внутри OnAuthorization это filterContext.HttpContext.User.Identity.Name. Я не хочу снова редактировать свой ответ. Вы можете просто заменить идентификатор соединения на имя идентичности практически везде. - person halter73; 09.02.2013
comment
Кстати, BasecampAuthorizeAttribute.OnAuthorization, скорее всего, будет вызван раньше, чем SessionTimerHub.OnConnected, поэтому вы, вероятно, захотите запустить таймер в первом методе, если таймер еще не существует в SessionTimer.Timers. Мой измененный ResetTimer после редактирования делает это. Очевидно, вам придется изменить его, чтобы использовать User.Identity.Name. - person halter73; 09.02.2013
comment
Наконец, я должен отметить, что каждый раз, когда вы вызываете $.connection.hub.start(), пользователь получает новый идентификатор соединения. Таким образом, пользователь будет иметь новый идентификатор подключения каждый раз, когда страница обновляется. Если вы создаете одностраничное приложение, это, вероятно, не проблема, но User.Identity.Name, безусловно, более стойкий. Скорее всего, вам понадобится другой словарь, который сопоставляет имена пользователей с идентификаторами подключения, чтобы вы могли отправлять сообщения клиенту, но я оставлю это в качестве упражнения для читателя. :) - person halter73; 09.02.2013
comment
Я так благодарен за ваши усилия, которые помогли мне решить мою проблему. Я просто хочу что-то очистить, я хотел бы использовать BasecampAuthorizeAttribute.OnAuthorization для сброса таймера, потому что это единственный способ (просто я думаю), который я знаю, отловить все запросы, отправленные на ActionResult через ajax $.post. И с этим я могу сказать, что пользователь совершает действие. - person fiberOptics; 11.02.2013
comment
Я также думаю, что если я смогу поймать все $.post события на странице, мне просто нужно будет вызвать timer.server.resetTimer, чтобы сбросить таймер. И мне больше не нужно использовать BasecampAuthorizeAttribute.OnAuthorization. Но пока я не очень разбираюсь в этом. - person fiberOptics; 11.02.2013

Уже есть таймер мира: часы пользователя. Позвольте собственному времени пользователя отсчитывать время, когда он вошел в систему: сохраните его личное время в файле cookie javascript.

Допустим, мы аутентифицируем пользователя с помощью ключа 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
person Henrik    schedule 09.02.2013

На главной странице:

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";
        }


    }
}
person sambasivarao    schedule 07.02.2013
comment
извините, но я думаю, что это не то, что я ищу. - person fiberOptics; 07.02.2013