springboot整合shiro实现记住我功能

 更新时间:2021年10月26日 09:38:02   作者:桐花思雨  
这篇文章主要介绍了springboot整合shiro实现记住我功能,配置类 ShiroConfig,通过实例代码给大家介绍的非常详细,需要的朋友可以参考下

前言

上一篇 文章我们完成了在 thymeleaf 模板引擎中使用 shiro 标签,也就是根据不同的用户身份信息,前端页面来显示不同的页面内容。本篇文章我们来完成在登录页面的记住我的功能

springboot 整合 shiro 之实现记住我

项目依然使用 springboot整合shiro 这个项目,稍稍改动即可完成记住我的功能

配置类 ShiroConfig

完整的代码如下

@Configuration
public class ShiroConfig {

    /**
     * 安全管理器
     *
     * @param userRealm userRealm
     * @return defaultWebSecurityManager
     */
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        // 实现记住我,所需要的配置
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
        return defaultWebSecurityManager;
    }

    /**
     * thymeleaf模板引擎中使用shiro标签时,要用到
     *
     * @return
     */
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 设置登录页面url
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        shiroFilterFactoryBean.setSuccessUrl("/user/index");
        shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");

        // 注意此处使用的是LinkedHashMap是有顺序的,shiro会按从上到下的顺序匹配验证,匹配了就不再继续验证
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        filterChainDefinitionMap.put("/layer/**", "anon");// 静态资源放行
        filterChainDefinitionMap.put("/img/**", "anon");
        filterChainDefinitionMap.put("/jquery/**", "anon");
        // add.html页面放行
        filterChainDefinitionMap.put("/user/add", "authc");
        // update.html必须认证
        filterChainDefinitionMap.put("/user/update", "authc");
        // index.html必须认证
        filterChainDefinitionMap.put("/user/index", "user");
        // 设置授权,只有user:add权限的才能请求/user/add这个url
        filterChainDefinitionMap.put("/user/add", "perms[user:add]");
        filterChainDefinitionMap.put("/user/update", "perms[user:update]");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

	// 实现记住我,所需要的配置
    @Bean
    public SimpleCookie simpleCookie() {
        // 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setHttpOnly(true);
        // 记住我cookie生效时间1小时,单位秒
        simpleCookie.setMaxAge(60 * 60);
        return simpleCookie;
    }

	// 实现记住我,所需要的配置
    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(simpleCookie());
        // rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }
}

login.html 登录页面

此时要拿到复选框 checkbox 是否被用户选中的状态值,选中为 true,未选中为 false,将这个状态值发生至后端登录接口

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <link rel="shortcut icon" type="image/x-icon" th:href="@{/img/favicon.ico}"/>
</head>
<body>
<form action="" method="post">
    <p>
        账号:
        <label><input type="text" class="username" name="username"></label>
    </p>
    <p>
        密码:
        <label><input type="text" class="password" name="password"></label>
    </p>
    <p>
        <label><input id="checkbox1" type="checkbox" name="rememberMe"></label>记住我
    </p>
    <p><button type="button" class="loginBtn">登录</button></p>
</form>
</body>
<script type="text/javascript" th:src="@{/jquery/jquery-3.3.1.min.js}"></script>
<script type="text/javascript" th:src="@{/layer/layer.js}"></script><!--layui的弹出层-->
<script type="text/javascript">
    $(document).ready(function () {
        $('.loginBtn').on('click', function () { // 登录按钮
            const username = $('.username').val();
            const password = $('.password').val();
            const rememberMe = $("input[type='checkbox']").is(':checked');
            $.ajax({// 用户登录
                type: 'post',
                url: '/user/doLogin',
                dataType: 'json',
                data: ({
                    'username': username,
                    'password': password,
                    'rememberMe': rememberMe
                }),
                success: function (resp) {
                    console.log(resp);
                    if (resp.code !== 200) {
                        layer.msg(resp.message, function () {// layui的弹窗
                        });
                    } else if (resp.code === 200) {
                        window.location.href = 'http://127.0.0.1:8080'+ resp.action;
                    }
                },
                error: function () {// 此处添加错误处理
                    layer.open({
                        title: '提示信息',
                        content: '后台访问错误,请联系管理员',
                        skin: 'layui-layer-molv',
                        icon: 0
                    });
                }
            });
        });
    });
</script>
</html>

controller

@Controller
@RequestMapping(path = "/user")
@Slf4j
public class UserController {

    @GetMapping(path = "/login")
    public String login() {
        return "login";
    }

    @GetMapping(path = "/index")
    public String index() {
        return "index";
    }

    @GetMapping(path = "/add")
    public String add() {
        return "add";
    }

    @GetMapping(path = "/update")
    public String update() {
        return "update";
    }

    @GetMapping(path = "/unauthorized")
    public String unauthorized() {
        return "unauthorized";
    }

    /**
     * 用户登录
     *
     * @param userVO
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "/doLogin")
    @ResponseBody
    public ResultMap doLogin(@NotNull @Valid UserVO userVO, @NotNull BindingResult bindingResult) {
        // ------参数校验------
        if (bindingResult.hasErrors()) {
            String message = Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage();
            log.info("校验的message信息为:" + message);
            return new ResultMap().fail().message(message);
        }
        // 将用户名,密码交给shiro
        UsernamePasswordToken token = new UsernamePasswordToken(userVO.getUsername(), userVO.getPassword(), userVO.getRememberMe());
        String msg;
        try {
            // shiro帮我们匹配密码什么的,我们只需要把东西传给它,它会根据我们在UserRealm里认证方法设置的来验证
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
            return new ResultMap().success().action("/user/index");
        } catch (AuthenticationException e) {
            if (e instanceof IncorrectCredentialsException) {
                msg = "密码错误";
            } else if (e instanceof LockedAccountException) {
                msg = "用户被禁用";
            } else if (e instanceof UnknownAccountException) {
                msg = "用户不存在";
            } else {
                msg = "用户认证失败";
            }
        }
        return new ResultMap().error().message(msg);
    }

    /**
     * 用户退出登录
     * 添加记住我功能了,退出登录时,除了要当前的subject退出之外,还要删除用户浏览器上的Cookie信息
     *
     * @return
     */
    @GetMapping(path = "/logout")
    public String logout(HttpServletResponse response) {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated()) {
            subject.logout();
            Cookie cookie = new Cookie("rememberMe", null);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }
        return "login";
    }
}

UserVO

public class UserVO implements Serializable {

    @NotBlank(message = "账号不能为空")
    private String username;

    @NotEmpty(message = "密码不能为空")
    private String password;

    private Boolean rememberMe;

	// 省略set/get方法
}

测试

我们以账号 jack 为例进行登录,如下

在这里插入图片描述

进入首页页面如下,再次查看 Cookies 数据

在这里插入图片描述

我们这时关闭这个首页页面,在浏览器地址栏输入 http://127.0.0.1:8080/user/index 再次进入首页页面,会发现如上图一样,可以顺利访问,说明我们的记住我功能已经实现。这时,可以再次在浏览器地址栏输入 http://127.0.0.1:8080/user/add,进入 add.html 页面,如下

在这里插入图片描述

Cookies 的有效期内,当你关闭浏览器之后,再次进入 add.html 页面时,无需登录直接就可以访问了,说明记住我功能已经实现了。在浏览器地址栏输入 http://127.0.0.1:8080/user/update,进入 update.html 页面,如下

在这里插入图片描述

说明账号 jack 没有权限访问 update.html 页面,可以看控制台 sql 日志

在这里插入图片描述

源码:springboot-shiro

到此这篇关于springboot整合shiro之实现记住我的文章就介绍到这了,更多相关springboot整合shiro之实现记住我内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java项目启动失败的问题及解决

    java项目启动失败的问题及解决

    这篇文章主要介绍了java项目启动失败的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Springboot单元测试编写实践

    Springboot单元测试编写实践

    在日常的开发过程中,为了提高代码的可靠性和健壮性,同时也是检测代码的质量,减少测试环节的问题,会对完成的业务功能代码编写单元测试,在本文中,将分享一些单元测试的实践和心得,需要的朋友可以参考下
    2023-11-11
  • Java中.divide()方法使用及注意事项详解

    Java中.divide()方法使用及注意事项详解

    divide方法就是bigdecimal类中的一个除法计算方法,由于该divide方法参数类型众多并且不易理解容易出现错误,这篇文章主要给大家介绍了关于Java中.divide()方法使用及注意事项的相关资料,需要的朋友可以参考下
    2024-03-03
  • 使用mybatis拦截器处理敏感字段

    使用mybatis拦截器处理敏感字段

    这篇文章主要介绍了mybatis拦截器处理敏感字段方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 关于mybatis使用${}时sql注入的问题

    关于mybatis使用${}时sql注入的问题

    这篇文章主要介绍了关于mybatis使用${}时sql注入的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • microlog4android将Android Log日志写到SD卡文件中实现方法

    microlog4android将Android Log日志写到SD卡文件中实现方法

    这篇文章主要介绍了microlog4android将Android Log日志写到SD卡文件中实现方法的相关资料,需要的朋友可以参考下
    2016-10-10
  • java自动生成ID号的方法

    java自动生成ID号的方法

    这篇文章主要介绍了java自动生成ID号的方法,涉及java生成ID号的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • 15个顶级Java多线程面试题(附答案)

    15个顶级Java多线程面试题(附答案)

    这篇文章主要为大家分享了15个顶级Java多线程面试题,考察面试者是否有足够的Java线程与并发知识,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-01-01
  • Java开发必会的Linux命令

    Java开发必会的Linux命令

    这篇文章主要介绍了Java开发必会的Linux命令,帮助大家更好地进行java开发,感兴趣的小伙伴们可以参考一下
    2015-12-12
  • 关于Java中的实体类要 implements Serializable的原因分析

    关于Java中的实体类要 implements Serializable的原因分析

    这篇文章主要介绍了Java中的实体类为什么要 implements Serializable,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06

最新评论