我们日常的记住密码功能, 实现思路如下:
可以看到, 是基于COOKIE进行操作的.
Shiro对页面访问的权限分为三个级别:
未认证 - 可访问的页面, 例如: 登录入口.html, 注册入口.html
记住我 - 可访问的页面, 例如: 个人信息.html
已认证 - 可访问的页面, 例如: 转账.html
而大概的流程如下:
为了方便测试, 我们修改/resource/templates/login.html文件内容如下:
<form action="/user/login" method="post">u: <input type="text" name="username"/><br>p: <input type="password" name="password"/><br>记住我: <input type="checkbox" name="rememberMe"><br><span th:text="${msg}" style="color:red"></span><input type="submit">
</form>
因为rememberMe是基于Cookie的, 所以我们需要在SecurityManager中增加CookieManager, 那么我们在ShiroAutoConfiguration中进行定义:
@Bean
public CookieRememberMeManager getRememberMeManager() { // 支持 RememberMe, 并设置 CookieCookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); // 让服务器检查 rememberMe 键, 这里必须设置simpleCookie.setMaxAge(60); // 60 秒后过期cookieRememberMeManager.setCookie(simpleCookie);return cookieRememberMeManager;
}@Bean
public SecurityManager securityManager(MyRealm myRealm, EhCacheManager ehCacheManager) { // 定义 SecurityManagerDefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(ehCacheManager);securityManager.setSessionManager(getDefaultSessionManager());securityManager.setRememberMeManager(getRememberMeManager()); // 设置 RememberMeManagerreturn securityManager;
}
随后我们在UserController::login页面中进行接收rememberMe传递来的数据:
@PostMapping("/login")
public ModelAndView login(String username, String password, @RequestParam(defaultValue = "false", required = false) String rememberMe) {ModelAndView modelAndView = new ModelAndView();try {userService.checkLogin(username, password, rememberMe); // 传递给 userServicemodelAndView.setViewName("index");} catch (Exception e) {modelAndView.addObject("msg", "登陆失败!");modelAndView.setViewName("login");} finally {return modelAndView;}
}
那么UserServiceImpl::checkLogin的定义如下:
public void checkLogin(String username, String password, String rememberMe) throws Exception {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);if ("on".equals(rememberMe)) { // 如果选中, 那么直接设置 rememberMe 为 truetoken.setRememberMe(true);}subject.login(token);
}
随后我们在ShiroAutoConfiguration中进行配置ShiroFilter的具体页面的权限分配:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);Map<String, String> hashMap = new HashMap<>();hashMap.put("/", "user"); // 设置为 记住我 访问/*** anon 未认证可访问* user 记住我可访问 (已认证也可以访问)* authc 已认证可访问* perms 必须具备具体的权限才可以进行访问* logout 退出登录*/hashMap.put("/login", "anon"); // login 支持匿名访问hashMap.put("/static/**", "anon"); // static 下的内容随便访问hashMap.put("/logout", "logout"); // 访问则退出登录hashMap.put("/kFind", "authc, perms[sys:k:find]"); // 当前用户已登录, 并且存在 sys:k:find 权限才允许访问shiroFilterFactoryBean.setFilterChainDefinitionMap(hashMap);shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setUnauthorizedUrl("/");return shiroFilterFactoryBean;
}
假设我们SESSION默认设置为1000毫秒就失效, 也就是1秒后就失效.
那么这个案例, 当我们wangwu登录后 (勾选记住我), 主页面是可以进行访问的 (因为设置了记住我), 但是功能模块却访问不了 (因为设置了已认证). 如图: