SpringBoot2 + Shiro 框架实现自动登录功能

‍‍

前言

小伙伴经常会登录一些网站,比如阿里云、博客园、码云这些网站,会发现她们的登录页面会有一个记住我的选项,只要你在登录的时候勾选上,下次开机打开浏览器直接是登录状态。

原理

其实原理很简单,在用户勾选记住我进行登录时,后台根据用户唯一标识生成一串加密字符串,然后写回给浏览器到 cookie 并存留一段时间,用户下次重新打开浏览器,会发现浏览器是记住你的。

关于 cookie,后台对用户对象序列化并加密,当重新请求时,后台能够获取反序列化且解密之后的用户对象。

集成

前台登录:

<form class="layui-form" action="javascript:void(0);">
    <div class="layui-form-item">
        <img class="logo" src="images/logo_new.png" />
        <div class="title">SP Admin</div>
        <div class="desc">
            青 岛 市 最 具 影 响 力 的 开源项目 之 一
        </div>
    </div>
    <div class="layui-form-item">
        <input name="username" type="text" placeholder="账 户" value="admin" hover class="layui-input"  />
    </div>
    <div class="layui-form-item">
        <input name="password" type="password" placeholder="密 码" value="admin" hover class="layui-input"  />
    </div>
    <div class="layui-form-item">
        <input placeholder="验证码" name="verCode" hover class="code layui-input layui-input-inline"  />
        <img src="/sys/captcha" class="codeImage" id="captchaImage"/>
    </div>
    <div class="layui-form-item">
        <input type="checkbox" name="rememberMe" value="true" title="记住密码" lay-skin="primary">
    </div>
    <div class="layui-form-item">
        <button class="pear-btn pear-btn-success login" lay-submit lay-filter="login">
            登 入
        </button>
    </div>
</form>

后台登录:

/**
 * 登录
 */
@PostMapping("/login")
@ResponseBody
public Result login(String username, String password,String verCode,
                    boolean rememberMe,HttpServletRequest request){
    try{
        if (CaptchaUtil.ver(verCode, request)) {
            Subject subject = ShiroUtils.getSubject();
            password = MD5Utils.encrypt(username, password);
            UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
            subject.login(token);
        }else{
            CaptchaUtil.clear(request);
            return Result.error(400,"验证码错误");
        }
    }catch (Exception e) {
        e.printStackTrace();
        return Result.error("登录失败");
    }
    return Result.ok("登录成功");
}

配置文件 ShiroConfig 追加以下代码:

@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean (SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    shiroFilterFactoryBean.setLoginUrl("/login.html");
    shiroFilterFactoryBean.setUnauthorizedUrl("/403");
    Map<String, Filter> filtersMap = new LinkedHashMap<>();
    filtersMap.put("kickout", kickoutSessionControlFilter());
    shiroFilterFactoryBean.setFilters(filtersMap);
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    /**
     * 静态文件
     */
    filterChainDefinitionMap.put("/css/**","anon");
    filterChainDefinitionMap.put("/lib/**","anon");
    filterChainDefinitionMap.put("/data/**","anon");
    filterChainDefinitionMap.put("/images/**","anon");
    filterChainDefinitionMap.put("/js/**","anon");
    filterChainDefinitionMap.put("/file/**","anon");

    /**
     * 登录注册
     */
    filterChainDefinitionMap.put("/login.html","anon");
    filterChainDefinitionMap.put("/sys/logout","anon");
    filterChainDefinitionMap.put("/sys/login","anon");
    filterChainDefinitionMap.put("/sys/register","anon");
    filterChainDefinitionMap.put("/sys/captcha","anon");

    /**
     *  authc:所有的url都不许,认证通过后才可以访问;
     *  anon:所有的url都可以匿名访问
     *  user:表示记住我或认证通过才可以访问
     */
    filterChainDefinitionMap.put("/**", "kickout,user");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
}

/**
 * cookie管理对象
 * @return
 */
@Bean
public CookieRememberMeManager rememberMeManager(){
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    cookieRememberMeManager.setCipherKey(Base64.decode("4BxVhuFKUs0KTA3Kprsdag=="));
    return cookieRememberMeManager;
}

@Bean
public SimpleCookie rememberMeCookie(){
    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    simpleCookie.setMaxAge(259200);
    return simpleCookie;
}

需要说明的是:

  • 如果是用户正常登录

subject.isRemembered()==false
subject.isAuthenticated()==true
  • 如果是通过记住我登录

subject.isRemembered()==true
subject.isAuthenticated()==false

另外对于拦截器的配置:

  • authc:所有的url都不许,认证通过后才可以访问

  • anon:所有的url都可以匿名访问

  • user:表示记住我或认证通过才可以访问

如果你开启了记住我的选项,需要使用 user 进行拦截。

案例

登录地址:https://tools.cloudbed.vip

账号密码:admin admin2020

记住密码登录,然后关闭浏览器,重新打开浏览器进入以上网址,如果直接进入后台说明记住成功。

源码:gitee.com/52itstyle/SPTools

暴力吃瓜之合并大西瓜小游戏!

阿里一面:如何保证API接口数据安全?

推荐一款高颜值的 Spring Boot 快速开发框架

推荐一个超级简单 Java 图形验证码模块

分享一个支付大屏实时监控数据平台

推荐一款清爽的实时监控大屏附安装教程

大屏监控 Metabase 集成到 Java 项目

一个超牛逼的 Java 文件在线预览项目

如何保障消息100%投递成功、消息幂等性


版权声明:本文为zhulin2012原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。