springboot + shiro 实现记住我

本文介绍了如何使用Apache Shiro框架实现网页的自动登录功能,即记住我。主要通过配置SimpleCookie、CookieRememberMeManager和FormAuthenticationFilter,设置cookie参数并处理安全性。在登录控制器中处理序列化用户对象,并在前端页面设置记住我选项。当用户选择记住我后,关闭并重新打开浏览器,系统能够自动登录。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:

       我们平常在登录网站的时候,经常会发现网页上有个 “下次自动登录” 或者 “记住我” 的功能,我们可以使用 shiro 来实现这个简单的功能。它主要是使用 cookie 来实现的。达到的最终效果就是第一次使用账号和密码登录(登录成功),此时退出浏览器,然后重新打开网页登录界面,就会自动登录到登录成功的网页上。

配置所需Bean:

       在 shiroConfig 类中添加 SimpleCookie CookieRememberMeManager FormAuthenticationFilter ,如下所示:

    @Bean
	public SimpleCookie rememberMeCookie(){
	    // 这个参数是 cookie 的名称,叫什么都行,我这块取名 rememberMe
	    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
	    // setcookie 的 httponly 属性如果设为 true 的话,会增加对 xss 防护的安全系数,
        // 只能通过http访问,javascript无法访问,防止xss读取cookie
	    simpleCookie.setHttpOnly(true);
	    simpleCookie.setPath("/");
	    // 记住我 cookie 生效时间30天 ,单位是秒
	    simpleCookie.setMaxAge(2592000);
	    return simpleCookie;
	}
	/**
	 * cookie管理对象;记住我功能,rememberMe管理器
	 * @return
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager(){
	    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
	    cookieRememberMeManager.setCookie(rememberMeCookie());
	    //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
	    cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
	    return cookieRememberMeManager;
	}
	
	/**
	 * FormAuthenticationFilter 过滤器 过滤记住我
	 * @return
	 */
	@Bean
	public FormAuthenticationFilter formAuthenticationFilter(){
	    FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
	    // 对应 rememberMeCookie() 方法中的 name
	    formAuthenticationFilter.setRememberMeParam("rememberMe");
	    return formAuthenticationFilter;
	}

       同样也是在 shiroConfig 中将 CookieRememberMeManager 注入到 SecurityManager 中,如下所示:

    @Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myShiroRealm());
		// 将 CookieRememberMeManager 注入到 SecurityManager 中,否则不会生效
		securityManager.setRememberMeManager(rememberMeManager());
		// 将 sessionManager 注入到 SecurityManager 中,否则不会生效
		securityManager.setSessionManager(sessionManager());
		return securityManager;
	}

       同样也是在 shiroConfig 中将 ShiroFilterFactoryBean 的认证属性改变,由 authc 改为 user ,如下所示:

    @Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
		// Shiro的核心安全接口,这个属性是必须的
		shiroFilter.setSecurityManager(securityManager);		
		//不输入地址的话会调用 /login 对应的地址
		shiroFilter.setLoginUrl("/login");
		Map<String, String> map = new LinkedHashMap<>();
		map.put("/login", "anon");
		map.put("/static/**", "anon");		
        // 对所有用户认证, authc 表示需要认证才能进行访问; user 表示配置记住我或认证通过可以访问的地址
		map.put("/**", "user");//authc
		shiroFilter.setFilterChainDefinitionMap(map);
		return shiroFilter;
	}

       看下 LoginController 类是如何实现,需要注意的是 User 这个实体类必须要实现序列化接口,否则会报序列化异常,因为 Remember Me 会将用户信息加密然后以 cookie 保存。

@Controller
@Slf4j
public class LoginController {

	@Autowired
	UserService userService;
	
	@GetMapping("/login")
	public String login() {
		return "login";
	}
	
	@PostMapping("/login")
	public String login(User user) {
		if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
			 return "error";
		}
		//用户认证信息
		Subject subject = SecurityUtils.getSubject();
		UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
				user.getUserName(),
				user.getPassword()
		,user.isRememberMe());
		
		//usernamePasswordToken.setRememberMe(true);
		try {
			// 用户账号和密码进行验证
			subject.login(usernamePasswordToken);
		} catch (UnknownAccountException e) {
			log.error("用户名不存在!", e);
			 return "error";
		} catch (AuthenticationException e) {
			log.error("账号或密码错误!", e);
			 return "error";
		} catch (AuthorizationException e) {
			log.error("没有权限!", e);
			 return "error";
		}
		 return "shiro_index";
	}
	
	@GetMapping("/page_skip")
	public String page_skip() {
		return "page_skip";
	}
	
	@RequestMapping("/shiro_index")
	public ModelAndView shiro_index() {
		ModelAndView view = new ModelAndView();
		
		view.setViewName("shiro_index");
		return view;
	}  
  
}

       看下前端页面 login.jsp 是如何实现,说白了就是携带参数,将请求发送到 Controller 进行拦截,如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>一路发咨询网站</title>
</head>
<body>
<script type="text/javascript" src="/static/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="/static/js/login.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/login.css"/>
<h1>欢迎登录一路发咨询网站</h1>

<form action="/login">
	<div id="father" style="background-image: url('/static/image/index.png');">
		<div style="width:300px;height:100px;">
			<div style="width:150px;float:left">
				<span>用户名:<span></span>
			</div>
			<div style="width:150px;float:left;">
				<input  style="height:34px" type="text" name="userName"/>
			</div>
		</div>
		<div style="width:300px;height:100px;">
			<div style="width:150px;float:left;">
				<span>密码:<span></span>
			</div>
			<div style="width:150px;float:left;">
				<input  style="height:34px" type="password" name="password"/>
			</div>
		</div>
		<div style="width:300px;height:100px;">
			<div style="width:64px;float:left;margin-left:280px">
				<input style="height:34px;width:34px;" type="checkbox" name="rememberMe" />
			</div>
			 <div style="width:150px;float:left;margin-top:-4px">
					<span>记住我<span></span>
			</div>
		</div>
		 <div style="margin-left:190px">
				<input style="height:50px;width:90px;font-size:34px;font-weight:bold" type="submit" value="提交"/>
		</div>
	</div>
</form>
</body>
</html>

测试:

       此时,启动工程,输入网址 http://localhost:8080 ,第一次登录的时候,需要用户自己输入用户名和密码,选中记住的标签。登录成功之后,此时关掉浏览器,再打开浏览器,再次输入刚才的网址,即可发现这次登录不需要再输入用户名和密码,即可直接进入验证通过的界面。

cookie查看:

       当我们第二次不输入账号和密码进入网页时,我们打开 F12 ,看下浏览器帮我们保存的 cookie 长什么样子,如下所示,其实就是将用户信息加密(包含密码)后放到前端,由于在项目中 user 对象信息过于庞大,不能全部存入 cookie ,且 cookie 对长度有一定的限制。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值