После последнего исследования«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);
}
На следующем рисунке показан процесс отладки кода подтверждения входа в систему.
3. Исходный код
Адрес источника:spring-boot-23-shiro-rememberДобро пожаловать, пометьте, разветвите и поощрите автора
Новичок также должен стать архитектором и много работать вместе.
Добро пожаловать, чтобы обратить внимание на мой публичный аккаунт WeChat [птицы не гадят]
Спасибо, учитесь вместе, развивайтесь вместе и становитесь отличным человеком