предисловие
Только лысая голова может стать сильнее.
Текст был включен в мой репозиторий GitHub, добро пожаловать, звезда:GitHub.com/Zhongf UC очень…
До стажировки я уже смотрел, что такое single sign-on, но во время стажировки был занят другими делами, поэтому в фаворитах лежало несколько сайтов:
Некоторое время назад ко мне пришел читатель, чтобы представить рукопись, использующую JWT для реализации единого входа (но в статье не рассказывается, что такое единый вход), поэтому я думаю, что пришло время разобраться.
1. Что такое единый вход?
Английское название единого входа: Single Sign On (сокращение отSSO).
существуетНовичок/Докогда мы обычноединая система, все функции находятся в одной системе.
Позже мыРазумное использование ресурсов и снижение связанности, поэтому единая системарасколотьна несколько подсистем.
- обзор:Распределенные основы
такие как АлиТаобао и Тмолл, очевидно, мы можем знать, что это две системы, но когда вы их используете, если вы входите в Tmall, Taobao также войдет в систему автоматически.
Проще говоря, единый входВ нескольких системах пользователю нужно войти в систему только один раз, и каждая система может воспринимать, что пользователь вошел в систему.
2. Просмотрите единый вход в систему
Когда я впервые изучал JavaWeb, вход в систему и регистрация были функциями, которые я использовал больше всего (я делал это, когда впервые изучал сервлет, я делал это, когда изучал SpringMVC, я делал это, когда следил за проектом...), я все равно не сосчитать.Сколько раз я выполнял функции входа и регистрации... Вот краткое описание того, как мы делали функцию входа в систему, когда мы только учились.
Как мы все знаем, HTTPнет статусасоглашение, а значитСервер не может подтвердить данные пользователя. В итоге W3C предложил: выдавать пропуск каждому пользователю, и независимо от того, кто заходит, им необходимо иметь при себе пропуск, чтобы сервер мог подтвердить информацию о пользователе из пропуска. ПропускCookie.
Если файл cookie должен проверить «паспорт» пользователя для подтверждения личности пользователя, то сеанс должен подтвердить личность пользователя, проверив «список клиентов» на сервере.Сессия эквивалентна созданию «списка клиентов» на сервере..
Протокол HTTP не имеет состояния, и сеанс не может определить, является ли он одним и тем же пользователем на основе HTTP-соединения. Итак: сервер отправляет файл cookie с именем JESSIONID в браузер пользователя, и его значение является значением идентификатора сеанса.На самом деле сеанс основан на файлах cookie, чтобы определить, является ли это одним и тем же пользователем..
Поэтому мы обычно делаем следующее, чтобы реализовать вход в единую систему:
-
Авторизоваться: сохранить информацию о пользователе в объекте Session.
- Если его можно найти в объекте Session, это означает, что вы вошли в систему.
- Если он не найден в объекте Session, это означает, что вы не вошли в систему (или вышли из системы)
- выйти (выйти из системы): удалить информацию о пользователе из сеанса
- Запомнить меня (после закрытия браузера снова откройте браузер, чтобы оставаться в системе): используется с файлами cookie
Для кода моей предыдущей демонстрации вы можете обратиться к:
/**
* 用户登陆
*/
@PostMapping(value = "/user/session", produces = {"application/json;charset=UTF-8"})
public Result login(String mobileNo, String password, String inputCaptcha, HttpSession session, HttpServletResponse response) {
//判断验证码是否正确
if (WebUtils.validateCaptcha(inputCaptcha, "captcha", session)) {
//判断有没有该用户
User user = userService.userLogin(mobileNo, password);
if (user != null) {
/*设置自动登陆,一个星期. 将token保存在数据库中*/
String loginToken = WebUtils.md5(new Date().toString() + session.getId());
user.setLoginToken(loginToken);
User user1 = userService.userUpload(user);
session.setAttribute("user", user1);
CookieUtil.addCookie(response,"loginToken",loginToken,604800);
return ResultUtil.success(user1);
} else {
return ResultUtil.error(ResultEnum.LOGIN_ERROR);
}
} else {
return ResultUtil.error(ResultEnum.CAPTCHA_ERROR);
}
}
/**
* 用户退出
*/
@DeleteMapping(value = "/session", produces = {"application/json;charset=UTF-8"})
public Result logout(HttpSession session,HttpServletRequest request,HttpServletResponse response ) {
//删除session和cookie
session.removeAttribute("user");
CookieUtil.clearCookie(request, response, "loginToken");
return ResultUtil.success();
}
/**
* @author ozc
* @version 1.0
* <p>
* 拦截器;实现自动登陆功能
*/
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
User sessionUser = (User) request.getSession().getAttribute("user");
// 已经登陆了,放行
if (sessionUser != null) {
return true;
} else {
//得到带过来cookie是否存在
String loginToken = CookieUtil.findCookieByName(request, "loginToken");
if (StringUtils.isNotBlank(loginToken)) {
//到数据库查询有没有该Cookie
User user = userService.findUserByLoginToken(loginToken);
if (user != null) {
request.getSession().setAttribute("user", user);
return true;
} else {
//没有该Cookie与之对应的用户(Cookie不匹配)
CookieUtil.clearCookie(request, response, "loginToken");
return false;
}
} else {
//没有cookie、也没有登陆。是index请求获取用户信息,可以放行
if (request.getRequestURI().contains("session")) {
return true;
}
//没有cookie凭证
response.sendRedirect("/login.html");
return false;
}
}
}
}
Подытожим идею приведенного выше кода:
- Когда пользователь входит в систему, проверьте учетную запись пользователя и пароль
- Сгенерируйте токен и сохраните его в базе данных, а затем запишите токен в файл cookie.
- Сохранить пользовательские данные в сеансе
- Куки принесут при запросе, проверьте авторизовались ли вы, если уже авторизовались, отпустите
Если вы не понимаете студентов, рекомендуется просмотреть Session, Cookies и HTTP:
- Внедрить сеансовую технологию, API файлов cookie, подробное объяснение, приложение
- Введение в сессию, API, жизненный цикл, приложение, отличие от куки
- Что такое HTTP
3. Проблемы и решения мультисистемного входа
3.1 Сессия не разделяет проблему
Функция входа в одну систему в основном реализуется с использованием сеанса для сохранения информации о пользователе, но мы знаем, что может быть несколько Tomcat в нескольких системах, и сеанс зависит от Tomcat текущей системы, поэтому сеанс системы A и сеанс системы Bне разделяютиз.
Есть несколько решений для решения проблемы неразделения сессий между системами:
- Глобальная репликация сеанса кластера Tomcat (сеанс каждого кота в кластере полностью синхронизирован) [повлияет на производительность кластера, не рекомендуется]
- По запрошенному IPХэш-картаНа соответствующую машину (это эквивалентно тому, что запрошенный IP-адрес всегда будет обращаться к одному и тому же серверу) [Если сервер выйдет из строя, большая часть данных сеанса будет потеряна, не рекомендуется]
- Поместите данные сеанса в Redis (используйте Redis для имитации сеанса) [предположение】
- Если вы не знаете одноклассников Редиса, рекомендуется переехать (Коллекция Redis)
Мы можем поставить функцию входаИзвлечь индивидуальноВыходи и делай подсистему.
Логика SSO (система входа) следующая:
// 登录功能(SSO单独的服务)
@Override
public TaotaoResult login(String username, String password) throws Exception {
//根据用户名查询用户信息
TbUserExample example = new TbUserExample();
Criteria criteria = example.createCriteria();
criteria.andUsernameEqualTo(username);
List<TbUser> list = userMapper.selectByExample(example);
if (null == list || list.isEmpty()) {
return TaotaoResult.build(400, "用户不存在");
}
//核对密码
TbUser user = list.get(0);
if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
return TaotaoResult.build(400, "密码错误");
}
//登录成功,把用户信息写入redis
//生成一个用户token
String token = UUID.randomUUID().toString();
jedisCluster.set(USER_TOKEN_KEY + ":" + token, JsonUtils.objectToJson(user));
//设置session过期时间
jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);
return TaotaoResult.ok(token);
}
Когда другие подсистемы входят в систему,Запросите SSO (систему входа) для входа в систему и запишите возвращенный токен в файл cookie., в следующий раз принесите печенье:
public TaotaoResult login(String username, String password,
HttpServletRequest request, HttpServletResponse response) {
//请求参数
Map<String, String> param = new HashMap<>();
param.put("username", username);
param.put("password", password);
//登录处理
String stringResult = HttpClientUtil.doPost(REGISTER_USER_URL + USER_LOGIN_URL, param);
TaotaoResult result = TaotaoResult.format(stringResult);
//登录出错
if (result.getStatus() != 200) {
return result;
}
//登录成功后把取token信息,并写入cookie
String token = (String) result.getData();
//写入cookie
CookieUtils.setCookie(request, response, "TT_TOKEN", token);
//返回成功
return result;
}
Суммировать:
- Система SSO генерирует токен, сохраняет информацию о пользователе в Redis и устанавливает время истечения срока действия.
- Другие системы запрашивают систему SSO для входа в систему, получают токен, возвращенный SSO, и записывают его в файл cookie.
- Каждый раз, когда делается запрос, будет доставлен файл cookie, а перехватчик получит токен, чтобы определить, был ли он вошел в систему.
Здесь, на самом деле, мы обнаружим, что на самом деле есть два изменения:
- Извлеките функцию входа в систему как одну систему (SSO), а другие системы запрашивают SSO для входа в систему.
- Первоначально информация о пользователе хранилась в сеансе, но теперь информация о пользователе хранится в Redis.
3.2 Междоменная проблема с файлами cookie
Выше мы решили проблему, что сеансы нельзя расшарить, но на самом деле есть другая проблема.Файлы cookie не являются междоменными
Например, мы просим<https://www.google.com/>
, браузер автоматическиgoogle.com
Печенька доставлена вgoogle
сервер без установки<https://www.baidu.com/>
Печенька доставлена вgoogle
сервер.
Это означает,Из-за разных доменных имен, после того, как пользователь войдет в систему A, система A вернет файл cookie в браузер, и пользователь не заберет с собой файл cookie системы A, когда пользователь снова запросит систему B.
Существует несколько решений междоменной проблемы с файлами cookie:
- После того, как сервер записывает файл cookie клиенту, клиент анализирует файл cookie, анализирует маркер и затем запрашивает маркер вместе с ним.
- Несколько доменных имен совместно используют файлы cookie, и домен файла cookie устанавливается при записи клиенту.
- Сохраните токен в SessionStroage (нет междоменной проблемы без использования файлов cookie)
На данный момент мы уже можем реализовать единый вход.
3.3 Принцип CAS
Когда дело доходит до единого входа, вы обязательно увидите этот термин: CAS (Центральная служба аутентификации) Давайте поговорим о том, как работает CAS.
Если логин был извлечен в систему отдельно, мы все еще можем играть так. Теперь у нас есть две системы, которыеwww.java3y.com
иwww.java4y.com
, SSOwww.sso.com
Во-первых, пользователь хочет получить доступ к системе Awww.java3y.com
Ограниченные ресурсы (например, функция корзины покупок, для доступа к которой требуется вход), система Awww.java3y.com
Обнаружено, что пользователь не вошел в систему, поэтомуПеренаправление в центр аутентификации sso с собственным адресом в качестве параметра. Запрашиваемый адрес выглядит следующим образом:
www.sso.com?service=www.java3y.com
Центр аутентификации sso обнаруживает, что пользователь не вошел в систему, и направляет его на страницу входа.Пользователь вводит имя пользователя и пароль для входа в систему, и пользователь и центр аутентификации устанавливаются.Глобальная сессия (генерирует токен, записывает его в файл cookie и сохраняет в браузере)
В последствии удостоверяющий центрперенаправить обратно в систему A, и перенесите Токен в Систему А. Адрес перенаправления выглядит следующим образом:
www.java3y.com?token=xxxxxxx
Затем система А обращается в центр аутентификации sso, чтобы проверить правильность токена.Если он правильный, система А устанавливает локальный сеанс с пользователем (Создать сеанс). На данный момент система А и пользователь уже вошли в систему.
В этот момент пользователь хочет получить доступ к Системе B.www.java4y.com
Ограниченные ресурсы (такие как функция заказа, доступ к функции заказа возможен только после входа в систему), система Bwww.java4y.com
Обнаружено, что пользователь не вошел в систему, поэтомуПеренаправление в центр аутентификации sso с собственным адресом в качестве параметра. Запрашиваемый адрес выглядит следующим образом:
www.sso.com?service=www.java4y.com
Обратите внимание, поскольку перед пользователем и центром аутентификацииwww.sso.com
Установлена глобальная сессия (в это время файл cookie был сохранен в браузере), поэтому на этот раз система Bперенаправитьв удостоверяющий центрwww.sso.com
Файлы cookie разрешены.
Удостоверяющий центрСогласно принесенной кукиОбнаружено, что с пользователем установлена глобальная сессия, и центр аутентификацииперенаправить обратно в систему B, и перенесите Токен в Систему B. Адрес перенаправления выглядит следующим образом:
www.java4y.com?token=xxxxxxx
Затем система B обращается в центр аутентификации sso, чтобы проверить правильность токена.Если он правильный, система B устанавливает локальный сеанс с пользователем (Создать сеанс). На данный момент система B и пользователь уже вошли в систему.
Ввиду этого, по сути, сертификационный центр SSO похож напересадочная станция.
Использованная литература:
- Блог Woohoo.cn on.com/EZ real l IU/Afraid…
- Блог Woohoo.cn на.com/because to pulling guest/afraid/6…
- blog.CSDN.net/Java любит IP и…
Наконец
рад вывестигалантерейные товарыОбщедоступный номер технологии Java:Java3y. В публичном аккаунтеБолее 200 оригинальных статейТехнические статьи, обширные видеоресурсы, красивые карты мозга,Следуйте, чтобы получить его!
Я думаю, что моя статья хорошо написана, нажмитеотличный!