Широ - Интегрированная капча

задняя часть сервер Shiro Realm

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