Эта статья содержит только идеи и не предоставляет конкретную и полную реализацию (блоггер слишком ленив, чтобы разобраться). Если у вас есть какие -либо вопросы или вы хотите знать, вы можете отправить личное сообщение или комментарий.
фон
В традиционных Java Web Small и средних проектах сеанс обычно используется для временного хранения информации о сеансе, такой как информация о личности журнала. Этот механизм реализуется путем заимствования механизма Cookie's HTTP, но приложение более трудно сохранять и обмениваться информацией cookie каждый раз, когда он запрашивает, а традиционная сеанс не является кластером, поэтому общие сервисы бэкэнд приложения используют токены, чтобы отличить информацию о входе в систему пользователя.
Все знают механизм сеанса J2EE, который очень удобен в использовании и очень полезен в традиционных веб -приложениях Java. Тем не менее, некоторые проекты, которые можно использовать в интернет -проектах или кластерах, имеют некоторые проблемы, такие как проблемы сериализации, проблемы задержки синхронизации и т. Д., Поэтому нам нужен инструмент, который может решать проблемы кластера, которые похожи на сеансы.
план
Мы используем механизм кэша для решения этой проблемы. Более популярный Redis является базой данных памяти NOSQL и имеет механизм отказа кэша, который очень подходит для хранения данных сеанса. Строка токена должна быть возвращена клиенту по первому запросу, и клиент использует этот токен для идентификации личности каждый раз, когда он запрашивает в будущем. Чтобы быть прозрачным в области развития бизнеса, мы инкапсулируем пакеты, сделанные запросом и ответом приложения. Нам нужно только сделать некоторые уловки в классе инструмента HTTP -запроса клиента и фреймворкам сервера MVC. Модификация класса инструмента HTTP клиента очень проста, главным образом, инкапсуляция протокола сервера.
Идеи реализации
1. Сформулируйте протокол сообщения ответа запроса.
2. Протокол протокола анализа обрабатывает токеновые строки.
3. Используйте Redis для хранения для управления токенами и соответствующей информации сеанса.
4. Предоставьте API для хранения и получения информации о сеансе.
Мы объясним план реализации каждого шага.
1. Сформулируйте протокол сообщения ответа запроса.
Поскольку вы хотите инкапсулировать протокол сообщений, вам нужно рассмотреть, что такое общедоступное поле, что такое полевое поле, структуру данных сообщений и т. Д.
Общественные поля, запрашиваемые, как правило, включают токен, версию, платформу, модель, IMEI, источник приложения и т. Д., Среди которых токен является главным героем нашего на этот раз.
Общие поля ответа обычно включают токен, статус результата (успех, неудача), код результата (код), информация о результатах и т. Д.
Для структуры данных пакетов мы выбираем JSON, потому что JSON распространен, имеет хорошую визуализацию и имеет низкую занятость байтов.
Сообщение запроса выглядит следующим образом, а корпус хранит бизнес-информацию, такую как зарегистрированное имя пользователя и пароль и т. Д.
{"token": "Client Token", / ** Номер версии клиента* / "версия": 11, / ** Тип платформы клиента* / "платформа": "ios", / ** Модель клиентского устройства* / "MachineModel": "iPhone 6s", "Imei": "Client String Number (мобильный телефон)", / ** Реальное тело должно быть Map* / "" / "": {"keie1", "" "key21": "value21"}, "key3": [1,]}}Отзывчивое сообщение
{ / ** Успех* / «Успех»: false, / ** Каждый запрос вернется в токен, а клиент должен использовать последний токен для каждого запроса* / «токен»: «Выбранный токен сервера для текущего запроса», / ** Неудачный код* / «Failcod }}2. Протокол протокола анализа обрабатывает токеновые строки.
Для серверной структуры MVC мы используем структуру SpringMVC, которая также является общим и не будет описана.
Давайте не будем упоминать о обработке токенов на данный момент. Во -первых, как передавать параметры после формулирования пакета.
Поскольку информация о запросе инкапсулируется, чтобы структура SpringMVC правильно внедрила необходимые нам параметры в контроллере, нам необходимо проанализировать и преобразовать пакеты.
Чтобы проанализировать информацию о запросе, нам необходимо настроить конвертер параметров SpringMVC. Реализуя интерфейс интерфейса HandlermethodargumentResolver, мы можем определить преобразователь параметров
Запрос BodyResolver реализует метод разрешения и вводит параметры. Следующий код является примером кода, и не используйте его напрямую.
@Override Public Object Resolveargument (параметр MethodParameter, ModelAndViewContainer MavContainer, NativeWebRequest WebRequest, WebDataBinderFactory BinderFactory) Throws Exception {String requestBodyStr = WebRequest.getParameter (requestPayPARAMNAM if (stringUtils.isnotblank (requestBodyStr)) {string paramname = parameter.getParameterName (); // Получить имя параметра в классе контроллера <?> ParamClass = parameter.getParameterType (); // Получить тип параметра в Controller/* parsesEs packets через json -инструмент*/jsonnode jsonnode jsonnode jsonnode. if (paramclass.equals (serviceRequest.class)) {// ServiceRequest - это VO, соответствующий пакету запроса ServiceRequest serviceRequest = objectMapper.readvalue (jsonnode.traverse (), serviceRequest.class); return serviceRequest; // возвращать этот объект в впрыскивание в параметр, он должен соответствовать типу, в противном случае исключение не будет легко поймано} if (jsonnode! = null) {// Найти параметры, необходимые в контроллере из сообщения jsonnode paramhsonnode = jsonnode.findvalue (paramname); if (paramjsonnode! = null) {return objectmapper.readvalue (paramjsonnode.travers (), paramclass); }}} return null; }Настройте конвертер параметров, который вы определили в файл конфигурации SrpingMVC
<MVC: Argents-Resolvers> <!-Объединенная информация о запросах обработка, извлечение данных из ServiceRequest-> <Bean id = "requestBodyResolver"> <name = "ObjectMapper"> <Bean> </bean> </properation> <!-Полевое имя, соответствующее ServiceRequest в запросе конфигурации. name = "requestBodyParamName"> <Deaturn> запрос cody </value> </property> </bean> </mvc: argery-resolvers>
Это позволяет правильно идентифицировать параметры в сообщении SpringMVC.
Далее мы должны обработать токен. Нам нужно добавить перехватчик SrpingMVC, чтобы перехватить каждый запрос. Это общая функция и не будет описана подробно.
Matcher M1 = pattern.compile ("/" token /":/"(.*?)/ ""). Matcher (requestBodystr); if (m1.find ()) {token = m1.group (1);} tokenMappool.VerifyToken (token); // Выполнять публичную обработку токена и проверитьТаким образом, вы можете получить токен, и вы можете сделать публичную обработку.
3. Используйте Redis для хранения для управления токенами и соответствующей информации сеанса.
На самом деле, он просто написал класс инструментов Redis Operation. Поскольку пружина используется в качестве основной структуры проекта, и мы не используем много функций Redis, мы напрямую используем функцию Cachemanager, предоставленную весной.
Настройка org.springframework.data.redis.cache.rediscachemanager
<!-Глобальные переменные Cache Manager и т. Д. Могут использоваться для доступа-> <Bean id = "cachemanager"> <Stustructor-Arg> <ref bean = "redistemplate"/> </constructor-arg> <property name = "usePrefix" value = "true"/> name = "cachepRefix"> <Bean> <constructor-arg-name = "delimiter" = "delimiter" = "delimiter" = "delimiter" = "delimiter" = "delimiter" = "delimiter" = "delimiter" = "cacheprefix"> <Bean> <vintructor-arg-arg. value = ":@webserviceinterface"/> </bean> </property> <name = "expires"> <!-Период достоверности кэша-> <Map> <purit> <ключ> <Dague> tokenPoolCache </value> </key> <!-TokenPool Cache-> <Daga> 2592000 </value> <!
4. Предоставьте API для хранения и получения информации о сеансе.
Благодаря вышеупомянутой прелюдии мы почти обработали токен. Далее мы будем реализовать работу по управлению токенами.
Нам нужно сделать разработку бизнеса удобной для сохранения и получения информации о сеансе, а токены прозрачны.
Импорт java.util.hashmap; import java.util.map; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.cache.cache; импорт org.spramework.cache.valuerapper; org.springframework.cache.cache.valuewrapper; import org.springframework.cache.cachemanager;/** * * * * * * * * * * * * * * * * * * * * * * * log = logfactory.getlog (tokenmappoolbean.class); / ** Токен, соответствующий текущему запросу*/ private threadlocal <string> currentToken; Частный Cachemanager Cachemanager; частная строковая кахенам; Частный токенгенератор Tokengenerator; public tokenmappoolbean (Cachemanager Cachemanager, String Cachename, Tokengenerator Tokengenerator) {this.cachemanager = cachemanager; this.cachename = cachename; this.tokengenerator = tokengenerator; currentToken = new Threadlocal <String> (); } /*** Если токен является законным, вернуть токен. Создайте новый токен и возвращает, * положите токен в Threadlocal и инициализируйте tokenMap * @param token * @return Token */ public String verifyToken (String Token) {// log.info("CheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC heCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheCheC VerifyedToken = null; (cache == null) {Выбросить новое runtimeexception ("Пул кэша, где хранится токен, не может быть Newtoken (); (Cache == NULL) {Throw Runtimeexception («Пул кэша, где хранится токен, не может быть Токен успешно:/"+newtoken+"/"Попробуйте сгенерировать:"+count+"times"); cachemanager.getcache (Cachename); {TokenMap = (Map <String, Object>) TokenMapWrapper.get (); /** * Установка в настоящее время, которую в настоящее время запрашивают атрибут и симулируйте атрибут: <br> * 1. Когда Cache.put (currentToken.get (), токен); Слишком большой и нуждается в оптимизации. cache.get (currenttoken.get ()); cache.get (currenttoken.get (); Текущий поток * @return Token */public String getToken () {if (currentToken.get () == nul Cache = cachemanager.getcache (Cachename); SetCachEmanager (Cachemanager Cachemanager) {this.cachemanager = cachemanager; Settokengenerator (Tokengenerator Tokengenerator) {this.tokengenerator = tokengenerator;Здесь используется переменная Threadlocal, потому что запрос соответствует потоку в контейнере сервлета, и она находится в одном и том же потоке в течение жизненного цикла запроса, и несколько потоков совместно используют диспетчер токена одновременно, поэтому эта локальная переменная поток необходима для сохранения строки токена.
Примечания:
1. Вызов, чтобы проверить метод, должен быть вызван в начале каждого запроса. И после того, как запрос будет выполнен, ясен вызван для очистки, чтобы не привести к выполнению проверки, но токен выведен из Threadlocal, когда он возвращается. (Эта ошибка беспокоила меня в течение нескольких дней, и коды проверки Ne Development не были обнаружены. Наконец, после тестирования я обнаружил, что перехватчик не был введен, когда произошло 404, поэтому я не назвал метод проверки, который вызвал токен в возвращаемом исключении, чтобы быть запрошенным токеном в прошлый раз, что привело к проблеме странного числа. Хорошо, запомните меня в большом котел).
2. Клиент должен сохранить каждый токен при инкапсулировании инструмента HTTP и использовать его для следующего запроса. Развитие компании IOS запросило аутсорсинг, но аутсорсинг не сделал так, как это необходимо. Когда не вошел в систему, токен не сохраняется. Каждый раз, когда токен передается, он является нулевым, в результате чего токен создается для каждого запроса, и сервер создает большое количество бесполезных токенов.
использовать
Метод использования также очень прост. Ниже приведены инкапсулированный менеджер входа в систему. Вы можете обратиться к приложению Token Manager для входа в систему Manager.
Import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.cache.cache; импорт org.springframework.cache.cache.valueWrapper; import org.spramework.cache.cachemanager; import com.niuxpper. LoginManager * Описание: LoginManager * Изменить запись: * @version v1.0 * @date 19 июля 2016 г. * @author niuxz * */public class loginmanager {private Static final log = logfactory.getlog (loginmanager.class); Частный Cachemanager Cachemanager; частная строковая кахенам; частный TokenMappoolbean tokenMappool; Public LoginManager (Cachemanager Cachemanager, String Cachename, TokenMappoolbean tokenMappool) {this.cachemanager = cachemanager; this.cachename = cachename; this.tokenmappool = tokenmappool; } public void login (String userId) {log.info ("user login: userId =" + userId); Cache cache = cachemanager.getcache (cachename); ValueWrapper valueWrapper = cache.get (userId); String token = (string) (valueWrapper == null? Null: valueWrapper.get ()); tokenmappool.removetokenmap (token); // rogin recored Перед выходом из системы tokenmappool.setattribute (constants.logged_user_id, userid); cache.put (userid, tokenmappool.gettoken ()); } public void LogoutCurrent (String phonetel) {String curuserid = getCurrentUserid (); log.info ("user vogout: userid =" + curuserid); tokenmappool.removetokenmap (tokenmappool.gettoken ()); // login if (curuserid! = null) {cache cache = cachemanager.getcache (cachename); cache.evict (curuserid); cache.evict (phonetel); }} / **. } public cachemanager getCachEmanager () {return cachemanager; } public String getCachename () {return cachename; } public tokenMappoolbean getTokenMappool () {return tokenMappool; } public void setCachEmanager (CachEmanager CacheManager) {this.cachemanager = cachemanager; } public void setCachename (String cachename) {this.cachename = cachename; } public void settekenmappool (tokenmappoolbean tokenmappool) {this.tokenmappool = tokenmappool; }}Ниже приведен общий интерфейс кода SMS -проверки. Некоторые приложения также используют сеанс для хранения кодов проверки. Я не рекомендую использовать этот метод. Недостатки хранения сеансов довольно велики. Просто посмотри на это, это было не то, что я написал
public void sendvalicodebyphonenum (string phonenum, string hintmsg, string logsuffix) {validatephonetimespace (); // Получить 6-битный случайный номер string string = codeUtil.getValidateCode (); log.info (code + "------>" + phonenum); // Вызовите код проверки SMS, чтобы отправить интерфейс retstatus retstatus = msgsendutils.sendsms (code + hintmsg, phonenum); if (! retstatus.getisok ()) {log.info (retstatus.toString ()); Бросьте новое ThrowStoDataException (ServicerPonsecode.fail_invalid_params, «Код проверки мобильного телефона не смог получить, попробуйте еще раз позже»); } // сбросить сеанс tokenmappool.setattribute (constants.validate_phone, phonenum); tokenmappool.setattribute (constants.validate_phone_code, code.tostring ()); tokenmappool.setattribute (constants.send_code_wrongnu, 0); tokenmappool.setattribute (constants.send_code_time, new Date (). getTime ()); log.info (logsuffix + phonenum + "SMS -проверка кода:" + code); }Обработка ответа
Некоторые студенты спросят, есть ли такая адаптивная упаковка сообщений?
@RequestMapping ("record")@responsebodypublic servicersponse record (строковое сообщение) {string userid = loginmanager.getCurrentUserid (); offysbordservice.recordMessage (userId, сообщение); return ServicerSponseBuilder.buildSuccess (null);}Среди них ServicerSponse - это инкапсулированный пакет ответов Vo. Нам просто нужно использовать аннотацию @Responsebody SpringMVC. Ключ - это строитель.
Import org.apache.commons.lang3.stringutils; import com.niuxz.base.pojo.serviceresponse; import com.niuxz.utils.spring.springcontextutil; import com.niuxz.web.server.token.tokenmappoolbean; ** * Класс. @date 25 апреля 2016 г. * @author niuxz * */public Class ServiceResponseBuilder {/** * Создание успешного ответного сообщения * * @param body * @return revicersponse с успешной операцией */public static servicersponse buildsuccess (объектный корпус) {return servicerSponse ((tokenmapponse) springcontexturetiltil .getToken (), «Успех действий», тело); } / ** * Создание успешного ответного сообщения * * @param body * @return Arevicerponse с успешной операцией * / public static servicersponse buildsuccess (string token, body объекта) {return new ServicerSponse (token, «Action успешно», Body); } / ** * Создание неудачного ответного сообщения * * @param failcode * msg * @return revicerponse, который сбой * / public static servicersponse buildfail (int failcode, string msg) {return buildfail (failcode, msg, null); } / ** * Создайте неудачное сообщение ответа * * @param Failcode * MSG Body * @return ServicerSponse, которая неудачная операция * / public static servicersponse buildfail (int failcode, строковое msg, тело объекта) {return new ServicerSponse (((tokenMappoolube) SpringContextUtil.getbean ("tokenMapponse").) StringUtils.isnotblank (MSG)? }}Поскольку мы используем форму статического класса инструментов, мы не можем внедрить объект TokenMappool (Token Manager) до весны, а затем мы можем получить его через API, предоставленный весной. Затем при построении информации о ответе напрямую вызовите метод getToken () TokenMappool. Этот метод вернет строку токена, связанную с текущим потоком. Опять же, важно позвонить в четкое вручную после окончания запроса (я называю его через глобальный перехватчик).
Приведенный выше пример управления информацией о сессии приложения, который подражает механизму сеанса J2EE, является всем контентом, которым я делюсь с вами. Я надеюсь, что вы можете дать вам ссылку, и я надеюсь, что вы сможете поддержать Wulin.com больше.