当前位置 博文首页 > 快乐的小三菊的博客:springboot + shiro 实现记住我

    快乐的小三菊的博客:springboot + shiro 实现记住我

    作者:[db:作者] 时间:2021-07-28 20:47

    背景:

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

    cs
    下一篇:没有了