0) Предисловие
Для предотвращения грубой силы входа в систему через программы и т. д. система добавит проверочный код при входе в систему, чтобы определить, следует ли войти в систему вручную или войти с помощью программы.
Принцип кода прост: запросите код подтверждения, сгенерированный сервером, когда пользователь заходит на страницу входа, сервер генерирует код подтверждения для сохранения изображения. СЕССИЯ генерирует код и отображается на странице входа, содержание изображения из-за нижней программы распознает вероятность успеха, и люди могут быстро идентифицировать элементы на изображении, тем самым уменьшая незаконную операцию входа в систему, не принадлежащую человеку.
С развитием технологий сегодня уровень успеха программного распознавания содержимого изображения становится все выше и выше, и появляется все больше и больше интерактивных форм кода проверки.В этой статье в качестве примера был взят простейший код проверки изображения, чтобы объяснить, как внедрить встроенный код проверки в Широ.
1) Интегрировать капчу
Kaptcha
Это плагин кода подтверждения с открытым исходным кодом от Google.Web.xml
встроенная конфигурацияServlet
Код подтверждения можно сгенерировать.
- Представляем библиотеку классов Kaptcha
"com.github.penggle:kaptcha:2.3.2"
- Web.xml Увеличение конфигурации сервлета Kaptcha
<!-- Kaptcha Servlet -->
<servlet>
<servlet-name>Kaptcha</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
<!-- 参数: 验证码图片高度 -->
<init-param>
<param-name>kaptcha.image.width</param-name>
<param-value>200</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Kaptcha</servlet-name>
<url-pattern>/kaptcha</url-pattern>
</servlet-mapping>
Kaptcha
Можно использовать встроенные свойстваinit-param
Для настройки часто используемая информация о конфигурации указана ниже.Для получения дополнительной информации обратитесь к официальной документации:
Атрибуты | значение |
---|---|
kaptcha.border | Если есть граница, по умолчанию |
kaptcha.border.color | Цвет границы, используйте значение RGB или белый, красный и другие цвета один раз, черный по умолчанию |
kaptcha.border.thickness | Ширина границы, по умолчанию 1 пиксель |
kaptcha.image.width | Ширина изображения капчи |
kaptcha.image.height | Высота изображения капчи |
kaptcha.textproducer.char.string | Содержимое символов капчи, по умолчанию abcde2345678gfynmnpwx |
kaptcha.textproducer.char.length | Код Количество символов, по умолчанию 5 |
kaptcha.textproducer.font.names | Название шрифта, по умолчанию Arial/Courier |
kaptcha.textproducer.font.size | размер шрифта, по умолчанию 40px |
kaptcha.textproducer.font.color | цвет шрифта, по умолчанию черный |
kaptcha.session.key | KEY при сохранении SESSION, по умолчанию KAPTCHA_SESSION_KEY |
kaptcha.background.clear.from/to | Начальный/конечный цвет градиента фона, значение цвета RGB, по умолчанию светло-серый/белый |
- новый
Controller
, Получите значение проверочного кода из SESSION. КЛЮЧ капчи в SESSION:KAPTCHA_SESSION_KEY
/**
* 获取验证码
*
* @author atd681
* @since 2018年8月13日
*/
@GetMapping("/kaptcha/get")
@ResponseBody
public String getKaptcha(HttpSession session) {
// Kaptcha生成验证后保存SESSION中的KEY为KAPTCHA_SESSION_KEY
return (String) session.getAttribute("KAPTCHA_SESSION_KEY");
}
Код подтверждения добавлен, запускаем проект:
-
доступ
http://localhost:6789/kaptcha
, вы можете увидеть изображение кода подтверждения. -
доступ
http://localhost:6789/kaptcha/get
, два запроса находятся в одном и том же SESSION, значение кода подтверждения в SESSION совпадает с содержимым изображения
2) На странице входа отображается проверочный код
<form action="/login" method="post">
<input type="text" name="username" placeholder="用户名" value="" />
<input type="password" name="password" placeholder="密码" value="" />
<!-- 增加验证码输入框 -->
<input type="text" name="captchaCode" placeholder="验证码" value="" />
<input type="submit" value="立即登录" />
</form>
<!-- 验证码,请求地址为在Web.xml中配置的Kaptcha内置的Servlet-->
<!-- Kaptcha Servlet生成验证码保存至SESSION并将图片返回 -->
<img src="/kaptcha" />
Посетите страницу входа, появится поле ввода кода подтверждения и изображение кода подтверждения.
3) Расширенный токен
существует[Сертификат Широ]Как упоминалось выше, является ли проверка входа законной или нет, находится вRealm
Достигается, а значит и в кодыRealm
Для проверки Широ инкапсулирует имя пользователя и пароль, отправленные для входа в систему, вUsernamePasswordToken
перейти кRealm
Средний. ПросмотрUsernamePasswordToken
Найдите и сохраните поля без проверочного кода в этом классе, поэтому вам нужно переопределитьToken
Код подтверждения можно сохранить.
private String username;
private char[] password;
private boolean rememberMe = false;
private String host;
новыйCaptchaToken
наследоватьUsernamePasswordToken
, существуетCaptchaToken
Просто добавьте поле кода подтверждения.
/**
* 扩展Shiro登录表单Token,增加验证码字段
*/
public class CaptchaToken extends UsernamePasswordToken {
// 序列化ID
private static final long serialVersionUID = -2804050723838289739L;
// 验证码
private String captchaCode;
/**
* 构造函数
* 用户名和密码是登录必须的,因此构造函数中包含两个字段
*/
public CaptchaToken(String username, String password, String captchaCode) {
// 父类UsernamePasswordToken的构造函数,后两个参数暂不需要, 不设置
super(username, password, false, "");
this.captchaCode = captchaCode;
}
/**
* 获取验证码
*/
public String getCaptchaCode() {
return captchaCode;
}
}
4) Широ использует CaptchaToken
Широ использует значение по умолчанию при создании токена.UsernamePasswordToken
, существуетFormAuthenticationFilter
КатегорияcreateToken
метод создан.
новыйCaptchaFormAuthenticationFilter
наследоватьFormAuthenticationFilter
и переписатьcreateToken
метод с использованиемCaptchaToken
и установите проверочный код.
/**
* 自定义认证过滤器
*/
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
/**
* 构造Token,重写Shiro构造Token的方法,增加验证码
*/
@Override
protected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) {
// 获取登录请求中用户输入的验证码
String captchaCode = request.getParameter("captchaCode");
// 返回带验证码的Token,Token会被传入Realm, 在Realm中可以取得验证码
return new CaptchaToken(username, password, captchaCode);
}
}
- Создать токен находится в
FormAuthenticationFilter
Во встроенном методе входа Широ в родительском классе - Код подтверждения был передан на странице входа, которую можно получить непосредственно через Запрос.
- переписать
createToken
После этого Широ обнаружит, что метод переопределен при создании токена, и выполнит определенный метод создания токена. - использовать
CaptchaToken
Не забудьте установить имя пользователя и пароль, когдаRealm
Не удается получить логин и пароль в
5) Добавить проверку кода подтверждения в Realm
существует[Сертификат Широ]Как упоминалось выше, ошибка сбоя входа в систему Shiro выдается ненормальным образом. Широ предоставляет общие исключения ошибок, но не исключение ошибки кода проверки. Нам нужно настроить два исключения, связанных с кодом проверки.
-
Код подтверждения пуст:
CaptchaEmptyException
-
Ошибка кода подтверждения:
CaptchaErrorException
/**
* 自定义验证码为空异常
* AuthenticationException为Shiro认证错误的异常,不同错误类型继承该异常即可
*/
public class CaptchaEmptyException extends AuthenticationException {
}
/**
* 自定义验证码错误异常
* AuthenticationException为Shiro认证错误的异常,不同错误类型继承该异常即可
*/
public class CaptchaErrorException extends AuthenticationException {
}
Добавьте непустой проверочный код и проверку правильности в Realm и создайте указанное выше исключение, если проверка не пройдена.Если родительский класс выдается во время процесса входа в систему,AuthenticationException
Исключение, Широ считает, что вход не выполнен. Запишите информацию об исключении и выполните логику ошибки входа.
// 获取用户信息的方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 在自定义的认证过滤器中将验证码保存至KaptchaCodeToken中
// 此处的Token就是认证过滤器中实例化的Token,可以直接强制转换
CaptchaToken captchaToken = (CaptchaToken) token;
// 获取用户在登录页面输入的验证码
String loginCaptcha = captchaToken.getCaptchaCode();
// 验证码未输入
if (loginCaptcha == null || "".equals(loginCaptcha)) {
// 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
// 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码为空
throw new CaptchaEmptyException();
}
// 获取SESSION中的验证码
// Kaptcha在生成验证码时会将验证码放入SESSION中
// 默认KEY为KAPTCHA_SESSION_KEY, 可以在Web.xml中配置
String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute("KAPTCHA_SESSION_KEY");
// 比较登录输入的验证码和SESSION保存的验证码是否一致
if (!loginCaptcha.equals(sessionCaptcha)) {
// 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
// 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码错误
throw new CaptchaErrorException();
}
// -----------------------------------------------------------------
// 以下是atd681-shiro-authc中的登录逻辑
// -----------------------------------------------------------------
}
6) Широ настроить пользовательский фильтр
- объявить пользовательский фильтр
// 使用自定义的表单认证过滤器
// 该过滤器中只是重写了Shiro的创建Token方法(增加了验证码)
authc(CaptchaFormAuthenticationFilter)
- Настройте все запросы на использование пользовательских фильтров
// 配置URL规则
// 有请求访问时Shiro会根据此规则找到对应的过滤器处理
filterChainDefinitionMap = [
"/kaptcha" : "anon", // 验证码不需要登录即可访问
"/kaptcha/get" : "anon", // 获取验证码不需要登录即可访问
"/login_success.jsp" : "anon", // 登录成功页不需要认证
"/**": "authc" // 其余所有页面需要认证(使用自定义的authc为过滤器)
]
-
authc
Для имени фильтра, если оно не объявлено, используйте имя Широ.FormAuthenticationFilter
, при объявлении использовать фильтр, объявленный в конфигурационном файле - Получение URL-адреса кода подтверждения не требует аутентификации.
7) Добавлена подсказка на страницу неудачного входа в систему.
<!-- 验证码异常 -->
<!-- 在登录的Realm中验证码校验错误时会抛出相关异常 -->
<c:if test="${shiroLoginFailure == 'com.atd681.shiro.kaptcha.CaptchaEmptyException'}">验证码为空</c:if>
<c:if test="${shiroLoginFailure == 'com.atd681.shiro.kaptcha.CaptchaErrorException'}">验证码不正确</c:if>
8) Пример кода
На данный момент конфигурация примера кода подтверждения на основе Shiro завершена.
-
Пример кода адреса:
https://github.com/atd681/alldemo
-
Пример названия проекта:
atd681-shiro-kaptcha