springboot + shiro 实现记住我

背景:

       我们平常在登录网站的时候,经常会发现网页上有个 “下次自动登录” 或者 “记住我” 的功能,我们可以使用 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 对长度有一定的限制。

Spring BootShiro 都是非常流行的Java开发框架。其中,Spring Boot是一个快速开发框架,能够快速地搭建一个Web应用程序;而Shiro是一个强大的安全框架,提供了认证、授权、加密、会话管理等安全相关的功能。 下面是实现Spring BootShiro权限管理的步骤: 1. 引入依赖 在pom.xml文件中引入Spring BootShiro的依赖。 ``` <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.0</version> </dependency> ``` 2. 编写Shiro配置类 编写一个Shiro配置类,用于配置Shiro的安全管理器、Realm、过滤器等。 ``` @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); // 设置过滤器链 Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); // 登录页面 shiroFilterFactoryBean.setLoginUrl("/login"); // 认证成功后跳转页面 shiroFilterFactoryBean.setSuccessUrl("/index"); // 未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized"); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } @Bean public Realm realm() { return new MyRealm(); } } ``` 3. 编写Realm 编写一个Realm类,用于进行认证和授权。 ``` public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = token.getUsername(); User user = userService.getUserByUsername(username); if (user == null) { throw new UnknownAccountException("用户不存在"); } return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName()); } // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String username = (String) principalCollection.getPrimaryPrincipal(); User user = userService.getUserByUsername(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addRoles(user.getRoles()); authorizationInfo.addStringPermissions(user.getPermissions()); return authorizationInfo; } } ``` 4. 编写Controller 编写一个Controller类,用于处理用户登录、登出等请求。 ``` @Controller public class LoginController { @GetMapping("/login") public String login() { return "login"; } @PostMapping("/login") public String doLogin(String username, String password, boolean rememberMe) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); token.setRememberMe(rememberMe); try { subject.login(token); return "redirect:/index"; } catch (AuthenticationException e) { return "login"; } } @GetMapping("/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); subject.logout(); return "redirect:/login"; } @GetMapping("/index") public String index() { return "index"; } @GetMapping("/unauthorized") public String unauthorized() { return "unauthorized"; } } ``` 5. 编写页面 编写登录页面、首页、未授权页面等页面。 ``` <!-- 登录页面 --> <form method="post" action="/login"> <input type="text" name="username" placeholder="用户名" required> <input type="password" name="password" placeholder="密码" required> <div> <input type="checkbox" name="rememberMe" id="rememberMe"> <label for="rememberMe">记住我</label> </div> <button type="submit">登录</button> </form> <!-- 首页 --> <h1>欢迎访问首页</h1> <!-- 未授权页面 --> <h1>您没有访问该页面的权限</h1> ``` 以上就是Spring BootShiro权限管理的实现步骤。通过配置Shiro的安全管理器、Realm、过滤器等,可以实现用户认证和授权。同时,通过在Controller中处理用户登录、登出等请求,可以实现用户的登录和退出功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

快乐的小三菊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值