springboot整合shiro实现记住我功能
作者:桐花思雨 发布时间:2023-07-29 20:21:34
前言
上一篇 文章我们完成了在 thymeleaf
模板引擎中使用 shiro
标签,也就是根据不同的用户身份信息,前端页面来显示不同的页面内容。本篇文章我们来完成在登录页面的记住我的功能
springboot 整合 shiro 之实现记住我
项目依然使用 springboot整合shiro 这个项目,稍稍改动即可完成记住我的功能
配置类 ShiroConfig
完整的代码如下
@Configuration
public class ShiroConfig {
/**
* 安全管理器
*
* @param userRealm userRealm
* @return defaultWebSecurityManager
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
// 实现记住我,所需要的配置
defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
return defaultWebSecurityManager;
}
/**
* thymeleaf模板引擎中使用shiro标签时,要用到
*
* @return
*/
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 设置登录页面url
shiroFilterFactoryBean.setLoginUrl("/user/login");
shiroFilterFactoryBean.setSuccessUrl("/user/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");
// 注意此处使用的是LinkedHashMap是有顺序的,shiro会按从上到下的顺序匹配验证,匹配了就不再继续验证
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/layer/**", "anon");// 静态资源放行
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/jquery/**", "anon");
// add.html页面放行
filterChainDefinitionMap.put("/user/add", "authc");
// update.html必须认证
filterChainDefinitionMap.put("/user/update", "authc");
// index.html必须认证
filterChainDefinitionMap.put("/user/index", "user");
// 设置授权,只有user:add权限的才能请求/user/add这个url
filterChainDefinitionMap.put("/user/add", "perms[user:add]");
filterChainDefinitionMap.put("/user/update", "perms[user:update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
// 实现记住我,所需要的配置
@Bean
public SimpleCookie simpleCookie() {
// 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true);
// 记住我cookie生效时间1小时,单位秒
simpleCookie.setMaxAge(60 * 60);
return simpleCookie;
}
// 实现记住我,所需要的配置
@Bean
public CookieRememberMeManager cookieRememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie());
// rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
}
login.html
登录页面
此时要拿到复选框 checkbox
是否被用户选中的状态值,选中为 true
,未选中为 false
,将这个状态值发生至后端登录接口
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
<link rel="shortcut icon" type="image/x-icon" th:href="@{/img/favicon.ico}"/>
</head>
<body>
<form action="" method="post">
<p>
账号:
<label><input type="text" class="username" name="username"></label>
</p>
<p>
密码:
<label><input type="text" class="password" name="password"></label>
</p>
<p>
<label><input id="checkbox1" type="checkbox" name="rememberMe"></label>记住我
</p>
<p><button type="button" class="loginBtn">登录</button></p>
</form>
</body>
<script type="text/javascript" th:src="@{/jquery/jquery-3.3.1.min.js}"></script>
<script type="text/javascript" th:src="@{/layer/layer.js}"></script><!--layui的弹出层-->
<script type="text/javascript">
$(document).ready(function () {
$('.loginBtn').on('click', function () { // 登录按钮
const username = $('.username').val();
const password = $('.password').val();
const rememberMe = $("input[type='checkbox']").is(':checked');
$.ajax({// 用户登录
type: 'post',
url: '/user/doLogin',
dataType: 'json',
data: ({
'username': username,
'password': password,
'rememberMe': rememberMe
}),
success: function (resp) {
console.log(resp);
if (resp.code !== 200) {
layer.msg(resp.message, function () {// layui的弹窗
});
} else if (resp.code === 200) {
window.location.href = 'http://127.0.0.1:8080'+ resp.action;
}
},
error: function () {// 此处添加错误处理
layer.open({
title: '提示信息',
content: '后台访问错误,请联系管理员',
skin: 'layui-layer-molv',
icon: 0
});
}
});
});
});
</script>
</html>
controller
@Controller
@RequestMapping(path = "/user")
@Slf4j
public class UserController {
@GetMapping(path = "/login")
public String login() {
return "login";
}
@GetMapping(path = "/index")
public String index() {
return "index";
}
@GetMapping(path = "/add")
public String add() {
return "add";
}
@GetMapping(path = "/update")
public String update() {
return "update";
}
@GetMapping(path = "/unauthorized")
public String unauthorized() {
return "unauthorized";
}
/**
* 用户登录
*
* @param userVO
* @param bindingResult
* @return
*/
@PostMapping(path = "/doLogin")
@ResponseBody
public ResultMap doLogin(@NotNull @Valid UserVO userVO, @NotNull BindingResult bindingResult) {
// ------参数校验------
if (bindingResult.hasErrors()) {
String message = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
log.info("校验的message信息为:" + message);
return new ResultMap().fail().message(message);
}
// 将用户名,密码交给shiro
UsernamePasswordToken token = new UsernamePasswordToken(userVO.getUsername(), userVO.getPassword(), userVO.getRememberMe());
String msg;
try {
// shiro帮我们匹配密码什么的,我们只需要把东西传给它,它会根据我们在UserRealm里认证方法设置的来验证
Subject subject = SecurityUtils.getSubject();
subject.login(token);
return new ResultMap().success().action("/user/index");
} catch (AuthenticationException e) {
if (e instanceof IncorrectCredentialsException) {
msg = "密码错误";
} else if (e instanceof LockedAccountException) {
msg = "用户被禁用";
} else if (e instanceof UnknownAccountException) {
msg = "用户不存在";
} else {
msg = "用户认证失败";
}
}
return new ResultMap().error().message(msg);
}
/**
* 用户退出登录
* 添加记住我功能了,退出登录时,除了要当前的subject退出之外,还要删除用户浏览器上的Cookie信息
*
* @return
*/
@GetMapping(path = "/logout")
public String logout(HttpServletResponse response) {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
subject.logout();
Cookie cookie = new Cookie("rememberMe", null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
return "login";
}
}
UserVO
类
public class UserVO implements Serializable {
@NotBlank(message = "账号不能为空")
private String username;
@NotEmpty(message = "密码不能为空")
private String password;
private Boolean rememberMe;
// 省略set/get方法
}
测试
我们以账号 jack
为例进行登录,如下
进入首页页面如下,再次查看 Cookies
数据
我们这时关闭这个首页页面,在浏览器地址栏输入 http://127.0.0.1:8080/user/index
再次进入首页页面,会发现如上图一样,可以顺利访问,说明我们的记住我功能已经实现。这时,可以再次在浏览器地址栏输入 http://127.0.0.1:8080/user/add
,进入 add.html
页面,如下
在 Cookies
的有效期内,当你关闭浏览器之后,再次进入 add.html
页面时,无需登录直接就可以访问了,说明记住我功能已经实现了。在浏览器地址栏输入 http://127.0.0.1:8080/user/update
,进入 update.html
页面,如下
说明账号 jack
没有权限访问 update.html
页面,可以看控制台 sql
日志
源码:springboot-shiro
来源:https://blog.csdn.net/weixin_38192427/article/details/120928810


猜你喜欢
- 本文介绍为了实现高效并发,虚拟机对 synchronized 做的一系列的锁优化措施高效并发是从 JDK5 升级到 JDK6 后一项重要的改
- 场景:使用MyBatis批量查询(select)、批量插入(insert)、批量更新(update)、批量删除(delete)操作MySQL
- •强引用(FinalReference),在java中,有点像C++的指针,通过引用,可以对堆中的对象进行操作。强引用具备以下特点: 1.强
- 概要应同学邀请,演示如何使用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作。下面测试的是一个廉价机票预订
- 上标是指比同一行中其他文字稍高的文字,而下标是指比同一行中其他文字稍低的文字。在生活中,我们常见的平方米、立方米等符号以及化学中的各种元素符
- Unity中的PostProcessScene:深入解析与实用案例在Unity游戏开发中,我们经常需要对场景进行后处理,以实现更丰富的视觉效
- 依赖<dependency> <groupId>io.springfox</groupId> <a
- 在Android开发中,有时候可能会要用到碎纸机的效果,今天小编为大家整理好代码,一起来看看吧。首先来看下效果图实例代码xml<com
- 上一篇文章实现了微信登录的移动端功能,下面继续完善功能,实现微信登录服务端功能服务端登录功能实现在以往文章里已经实现了服务端mvc框架,而登
- View的平滑滚动效果什么是实现View的平滑滚动效果呢,举个简单的例子,一个View从在我们指定的时间内从一个位置滚动到另外一个位置,我们
- 某次,在大型的工程代码中,我这样调用:#include <iostream>using namespace std;namesp
- 为了引入这个概率 首先从需求说起 即:现有某Activity专门用于手机属性设置 那么应该如何做呢? 根据已学知识 很快一个念头闪过 即:A
- 问题,打一个页面cpu暴涨,打开一次就涨100%,一会系统就卡的不行了。排查方法,因为是线上的linux,没有用jvm监控工具rim链接上去
- 在 String 中提供了两个截取字符串的方法,一个是从指定位置截取到字符串结尾,另一个是截取指定范围的内容。下面对这两种方法分别进行介绍。
- 本文实例讲述了Android中资源文件用法。分享给大家供大家参考,具体如下:一、XML文件间资源文件的使用引用格式:attribute=&q
- 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启动的,而不需要另
- 一、NIOjava.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所
- 前言偶然逛国外博客,看到了一个介绍文字动画的库,进入 pub 一看,立马就爱上这个动画库了,几乎你能想到的文字动画效果它都有!现在正式给大家
- 这篇文章主要介绍了Java代码块与代码加载顺序原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋
- 这篇文章主要介绍了Java中遍历ConcurrentHashMap的四种方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一