Spring Boot2 (15): Широ помни меня, помни меня, проверочный код Капча

Spring Boot

После последнего исследования«SpringBoot2 (12): научит вас создавать систему безопасности Shiro, касаясь ваших рук», который реализует аутентификацию и авторизацию Широ. Сегодня я продолжу изучать Широ на этой основе, чтобы реализовать функцию «запомнить меня, запомнить меня» и проверочный код Kaptcha при входе в систему.

Запомнить меня: статус входа пользователя будет аннулирован при закрытии браузера до истечения срока действия файла cookie. После закрытия браузера вы можете снова посетить страницу входа в систему без входа в систему. Поскольку он реализован с помощью файлов cookie, он действителен только в том же браузере.

Код подтверждения Kaptcha: это плагин кода подтверждения Google с открытым исходным кодом, который реализует проверку и перехват кода подтверждения для входа в систему.

1. Запомни меня

Будет ли статус входа пользователя недействительным при закрытии браузера до истечения срока действия файла cookie. После закрытия браузера вы можете снова посетить страницу входа в систему без входа в систему. Поскольку он реализован с помощью файлов cookie, он действителен только в том же браузере.

Изменить ШироКонфиг

/**
 * 路径过滤规则
 * @return
 */
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
	ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
	shiroFilterFactoryBean.setSecurityManager(securityManager);
	// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
	shiroFilterFactoryBean.setLoginUrl("/login");
	shiroFilterFactoryBean.setSuccessUrl("/index");
	// 拦截器
	LinkedHashMap<String, String> map = new LinkedHashMap<>();
	// 配置不会被拦截的链接 顺序判断
	// 对静态资源设置匿名访问
	map.put("/static/**", "anon");
	map.put("/css/**", "anon");
	map.put("/js/**", "anon");

	// 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
	// 进行身份认证后才能访问
	// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
	// user指的是用户认证通过或者配置了Remember Me记住用户登录状态后可访问
	map.put("/**", "user");
	shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
	return shiroFilterFactoryBean;
}

Поскольку я сделал несколько стилей для страницы входа и добавил файл статического ресурса static, в это время я столкнулся с ямой, и страница, на которую ссылаетсяjsа такжеcssВсе они недействительны, и тогда нам нужно разрешить анонимность статических ресурсов в перехватчике Широ, потому что нас перехватывают.anonдоступ.

обратите внимание, что будетShiroFilterFactoryBeanизmap.put("/**", "authc");изменить наmap.put("/**", "user");user означает, что к пользователю можно получить доступ после прохождения аутентификации или настройки RememberMe для запоминания статуса входа пользователя.

В процессе решения я проверил некоторую информацию, не только дляcssа такжеjsвыпуска, также необходимоstaticтакже отпустить

По вопросам, связанным с перехватом статических ресурсов, вы можете обратиться сюда, чтобы узнать о:Spring Boot Shiro не может получить доступ к JS/CSS/IMG + пользовательский фильтр не может получить доступ к идеальному решению

Вернитесь, чтобы продолжить, вызовите SimpleCookie, настройте основные свойства файла cookie: имя и срок действия.

/**
 * cookie对象
 * @return
 */
public SimpleCookie rememberMeCookie() {
	// 设置cookie名称,对应login.html页面的<input type="checkbox" name="rememberMe"/>
	SimpleCookie cookie = new SimpleCookie("rememberMe");
	// 设置cookie的过期时间,单位为秒,这里为一天
	cookie.setMaxAge(86400);
	return cookie;
}

Имя в параметре SimpleCookie — это имя атрибута тега имени страницы.

Реализована конфигурация атрибута объекта Cookie, а также необходимо передатьCookieRememberMeManagerУправляй этим.

/**
 * cookie管理对象
 * rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
 * @return
 */
public CookieRememberMeManager rememberMeManager() {
	CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
	cookieRememberMeManager.setCookie(rememberMeCookie());
	// rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
	cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
	return cookieRememberMeManager;
}

Затем установите объект управления файлами cookie наSecurityManagerсередина:

@Bean
public SecurityManager securityManager() {
	DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
	// 设置realm
	securityManager.setRealm(authRealm());
	// 用户授权/认证信息Cache, 采用EhC//注入记住我管理器
	securityManager.setRememberMeManager(rememberMeManager());
	return securityManager;
}

Шифрование

Открытый текст, использованный в проекте «Spring Boot2 (12): Научим вас создавать фреймворк безопасности Shiro», здесь мы его обновляем и используем шифрование MD5.

Создайте новый класс инструмента шифрования MD5.

public class MD5Utils {

    private static final String SALT = "niaobulashi";

    private static final String ALGORITH_NAME = "md5";

    private static final int HASH_ITERATIONS = 2;

    public static String encrypt(String pwd) {
        String newPassword = new SimpleHash(ALGORITH_NAME, pwd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();
        return newPassword;
    }

    public static String encrypt(String username, String pwd) {
        String newPassword = new SimpleHash(ALGORITH_NAME, pwd, ByteSource.Util.bytes(username + SALT),
                HASH_ITERATIONS).toHex();
        return newPassword;
    }
    
    public static void main(String[] args) {
        System.out.println("MD5加密后的密文为:" + MD5Utils.encrypt("root", "root"));
    }
}

вSALTэто зашифрованная соль, которую можно определить самостоятельно.

В основном методе в соответствии с открытым текстом имени пользователя и пароля выведите окончательный зашифрованный зашифрованный текст, вставьте полученное содержимое в нашу базу данных и используйте его для последующих входов в систему.

Добавлена ​​страница входа и главная страница

Страница входа login.html

Добавить флажок «Запомнить меня»

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <link rel="stylesheet" th:href="@{/static/css/login.css}" type="text/css">
    <script th:src="@{/static/js/jquery-1.11.1.min.js}"></script>
</head>
<body>
<div class="login-page">
    <div class="form">
        <input type="text" placeholder="用户名" name="account" required="required"/>
        <input type="password" placeholder="密码" name="password" required="required"/>
        <p><input type="checkbox" name="rememberMe"/>记住我</p>
        <button onclick="login()">登录</button>
    </div>
</div>
</body>
<script th:inline="javascript">var ctx = [[@{/}]];</script>
<script th:inline="javascript">
    function login() {
        var account = $("input[name='account']").val();
        var password = $("input[name='password']").val();
        var rememberMe = $("input[name='rememberMe']").is(':checked');
        $.ajax({
            type: "post",
            url: ctx + "login",
            data: {
                "account": account,
                "password": password,
                "rememberMe": rememberMe
            },
            success: function(r) {
                if (r.code == 100) {
                    location.href = ctx + 'index';
                } else {
                    alert(r.message);
                }
            }
        });
    }
</script>
</html>

Статические ресурсы js и css можно посмотреть в исходном коде

登录页面

Homeindex.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<p>你好![[${user.getUsername()}]]</p>
<a th:href="@{/logout}">注销</a>
</body>
</html>

Уровень контроллера

На основе оригинала добавьте параметр RememberMe и выполните шифрование MD5 для имени пользователя и открытого текста пароля, чтобы получить зашифрованный текст.

интерфейс входа

/**
 * 登录操作
 * @param account
 * @param password
 * @param rememberMe
 * @return
 */
@PostMapping("/login")
@ResponseBody
public ResponseCode login(String account, String password, Boolean rememberMe) {
	logger.info("登录请求-start");
	password = MD5Utils.encrypt(account, password);
	Subject userSubject = SecurityUtils.getSubject();
	UsernamePasswordToken token = new UsernamePasswordToken(account, password, rememberMe);
	try {
		// 登录验证
		userSubject.login(token);
		return ResponseCode.success();
	} catch (UnknownAccountException e) {
		return ResponseCode.error(StatusEnums.ACCOUNT_UNKNOWN);
	} catch (DisabledAccountException e) {
		return ResponseCode.error(StatusEnums.ACCOUNT_IS_DISABLED);
	} catch (IncorrectCredentialsException e) {
		return ResponseCode.error(StatusEnums.INCORRECT_CREDENTIALS);
	} catch (AuthenticationException e) {
		return ResponseCode.error(StatusEnums.AUTH_ERROR);
	} catch (Throwable e) {
		e.printStackTrace();
		return ResponseCode.error(StatusEnums.SYSTEM_ERROR);
	}
}

выход из системы

/**
 * 登出
 * @return
 */
@GetMapping("/logout")
public String logout() {
	getSubject().logout();
	return "login";
}

Запустите проект и протестируйте его, вы увидите, что эффект выглядит следующим образом:

登录操作

2. Проверочный код Капча

kaptcha — очень полезный инструмент для генерации проверочного кода. С его помощью вы можете генерировать различные стили капчи, потому что это настраивается. Принцип работы kaptcha заключается в вызове com.google.code.kaptcha.servlet.KaptchaServlet для создания изображения. В то же время поместите сгенерированную строку кода подтверждения в HttpSession.

Официальный сайт капчи:code.Google.com/archive/dread/could…

Использование капчи легко настраивается:

  • Капча шрифт
  • Размер шрифта капчи
  • Цвет шрифта капчи
  • Диапазон содержания проверочного кода (цифры, буквы, китайские иероглифы!)
  • Размер изображения капчи, граница, толщина границы, цвет границы
  • Строка интерференции проверочного кода (вы можете унаследовать com.google.code.kaptcha.NoiseProducer, чтобы написать собственную интерференционную строку)
  • Стиль проверочного кода (стиль «рыбий глаз», 3D, обычное размытие... Конечно, он также может наследовать пользовательский стиль com.google.code.kaptcha.GimpyEngine)

Подробная настройка капчи

свойства объекта капчи эффект По умолчанию
kaptcha.border Есть ли граница По умолчанию истинно
kaptcha.border.color цвет границы Значение по умолчанию — Color.BLACK.
kaptcha.border.thickness толщина границы По умолчанию 1
kaptcha.producer.impl Генератор капчи По умолчанию используется DefaultKaptcha
kaptcha.textproducer.impl Генератор текста капчи По умолчанию используется DefaultTextCreator.
kaptcha.textproducer.char.string Диапазон содержимого текстового символа капчи По умолчанию используется abcde2345678gfynmnpwx.
kaptcha.textproducer.char.length Длина текста капчи в символах По умолчанию 5
kaptcha.textproducer.font.names Стиль шрифта текста капчи Song, Kai, Microsoft Yahei, по умолчанию используется новый шрифт ("Arial", 1, fontSize), новый шрифт ("Courier", 1, fontSize)
kaptcha.textproducer.font.size Размер символов текста капчи По умолчанию 40
kaptcha.textproducer.font.color Цвет символов текста капчи Значение по умолчанию — Color.BLACK.
kaptcha.textproducer.char.space Расстояние между символами в тексте капчи По умолчанию 2
kaptcha.noise.impl Объект генерации шума капчи По умолчанию DefaultNoise
kaptcha.noise.color Цвет шума капчи Значение по умолчанию — Color.BLACK.
kaptcha.obscurificator.impl ДВИГАТЕЛЬ В СТИЛЕ CAPTCHA По умолчанию используется WaterRipple.
kaptcha.word.impl Отрисовка текстового символа капчи По умолчанию используется DefaultWordRenderer.
kaptcha.background.impl Генератор фона капчи По умолчанию используется DefaultBackground
kaptcha.background.clear.from Градиент цвета фона капчи По умолчанию — Color.LIGHT_GRAY.
kaptcha.background.clear.to Градиент цвета фона капчи Значение по умолчанию — Color.WHITE.
kaptcha.image.width Ширина изображения капчи По умолчанию 200
kaptcha.image.height Высота изображения капчи По умолчанию 50

добавить зависимости maven

<!--验证码-->
<dependency>
	<groupId>com.github.penggle</groupId>
	<artifactId>kaptcha</artifactId>
	<version>2.3.2</version>
</dependency>

Добавлен конфигуратор стиля изображения капчи

Конкретная конфигурация может относиться к вышеуказанномудетали конфигурации капчи, для различных распространенных конфигураций.

@Configuration
public class KaptchaConfig {

    @Bean(name="captchaProducer")
    public DefaultKaptcha getKaptchaBean(){
        DefaultKaptcha defaultKaptcha=new DefaultKaptcha();
        Properties properties=new Properties();
        //验证码字符范围
        properties.setProperty("kaptcha.textproducer.char.string", "23456789");
        //图片边框颜色
        properties.setProperty("kaptcha.border.color", "245,248,249");
        //字体颜色
        properties.setProperty("kaptcha.textproducer.font.color", "black");
        //文字间隔
        properties.setProperty("kaptcha.textproducer.char.space", "1");
        //图片宽度
        properties.setProperty("kaptcha.image.width", "100");
        //图片高度
        properties.setProperty("kaptcha.image.height", "35");
        //字体大小
        properties.setProperty("kaptcha.textproducer.font.size", "30");
        //session的key
        //properties.setProperty("kaptcha.session.key", "code");
        //长度
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        //字体
        properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑");
        Config config=new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

Добавлен слой контроллера кода проверки изображения.

Это процесс создания потока изображения файла с использованием ServletOutPutStream для вывода окончательного изображения.

заявлено в начале@Resource(name = "captchaProducer"), который является bean-компонентом, настроенным при запуске конфигуратора стиля изображения капчи:captchaProducer.

@Controller
@RequestMapping("/captcha")
public class KaptchaController {

    private static final Logger logger = LoggerFactory.getLogger(KaptchaController.class);

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @GetMapping("/captchaImage")
    public ModelAndView getKaptchaImage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ServletOutputStream out = response.getOutputStream();
        try {
            HttpSession session = request.getSession();
            response.setDateHeader("Expires", 0);
            // Set standard HTTP/1.1 no-cache headers.
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            // Set IE extended HTTP/1.1 no-cache headers (use addHeader).
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            // Set standard HTTP/1.0 no-cache header.
            response.setHeader("Pragma", "no-cache");
            // return a jpeg
            response.setContentType("image/jpeg");
            // create the text for the image
            String capText = captchaProducer.createText();
            //将验证码存到session
            session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
            logger.info(capText);
            // 创建一张文本图片
            BufferedImage bi = captchaProducer.createImage(capText);
            // 响应
            out = response.getOutputStream();
            // 写入数据
            ImageIO.write(bi, "jpg", out);

            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

Обратите внимание, что поток должен быть закрыт в концеout.close()

Освободите перехват кода подтверждения изображения

После перезапуска вы обнаружите, что интерфейсный запрос кода подтверждения изображения недоступен, и он по-прежнему переходит на страницу входа в систему localhost:8081/login.

Поскольку перехватчик, настроенный Широ, не был выпущен, необходимоShiroConfigРазрешить анонимный доступ для запроса ресурсов в

map.put("/captcha/captchaImage**", "anon");

Добавьте код подтверждения изображения на страницу входа

<div class="login-page">
    <div class="form">
        <input type="text" placeholder="用户名" name="account" required="required"/>
        <input type="password" placeholder="密码" name="password" required="required"/>
        <p>
            <label>验证码<br/>
                <input type="text" name="validateCode" id="validateCode" class="validateCode" required="required"/>
                <a href="javascript:void(0);">
                    <img src="/captcha/captchaImage" onclick="this.src='/captcha/captchaImage?'+Math.random()"/>
                </a>
            </label>
        </p>
        <br>
        <p><input type="checkbox" name="rememberMe"/>记住我</p>
        <button onclick="login()">登录</button>
    </div>
</div>

надdivдля всех частей тела

я прошу/captcha/captchaImageдобавить случайное значение послеMath.random(). Это связано с тем, что клиентский браузер кэширует ресурс с тем же URL-адресом, поэтому он использует случайное число для повторного запроса. Это то же самое, что когда внешний интерфейс находится в сети, суффикс запроса изменит номер версии, и самые последние ресурсы могут быть получены, не требуя от клиента ручного обновления браузера.

验证码请求

Изменить интерфейс запроса на вход

В основном это делается для проверки кода подтверждения, сгенерированного в фоновом режиме, и сравнения его с кодом подтверждения, введенным на стойке регистрации, чтобы проверить, совпадает ли он.

Здесь вставлена ​​только логика проверки проверочного кода, а исходный код в конце статьи.

Как можно заметитьvalidateCodeЭто параметр, запрошенный внешним интерфейсом, сначала проверьте, не пуст ли он.

Затем получите код подтверждения, сгенерированный в фоновом режиме из сеанса.

Наконец, сравните, соответствует ли код подтверждения, введенный во внешнем интерфейсе, коду, сгенерированному в фоновом режиме.

//1、检验验证码
if(validateCode == null || validateCode == ""){
	return ResponseCode.error(StatusEnums.PARAM_NULL);
}
Session session = SecurityUtils.getSubject().getSession();
//转化成小写字母
validateCode = validateCode.toLowerCase();
String v = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
//还可以读取一次后把验证码清空,这样每次登录都必须获取验证码
//session.removeAttribute("_come");
if(!validateCode.equals(v)){
	return ResponseCode.error(StatusEnums.VALIDATECODE_ERROR);
}

На следующем рисунке показан процесс отладки кода подтверждения входа в систему.

kaptcha验证码校验

3. Исходный код

Адрес источника:spring-boot-23-shiro-rememberДобро пожаловать, пометьте, разветвите и поощрите автора


Новичок также должен стать архитектором и много работать вместе.

Добро пожаловать, чтобы обратить внимание на мой публичный аккаунт WeChat [птицы не гадят]

Спасибо, учитесь вместе, развивайтесь вместе и становитесь отличным человеком

img