使用SpringBoot+Shiro实现记住我功能

1、加入需要用到的包

<dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

2、编写application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

spring.thymeleaf.cache=false

3、在页面表单中添加 “记住我” 功能的多选框

<input type="checkbox" name="rememberMe"/>记住我

4、编写MyShiroRealm配置类

package com.demo.rememberme.config;

import com.demo.rememberme.entity.User;
import com.demo.rememberme.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import javax.annotation.Resource;

public class MyShiroRealm extends AuthorizingRealm {
    @Resource
    private UserService userService;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("1:身份认证-->MyShiroRealm.doGetAuthorizationInfo()");
        //获取用户输入的信息
        UsernamePasswordToken token=(UsernamePasswordToken)authenticationToken;
        String userName=token.getUsername();
        String userPassword=new String(token.getPassword());
        System.out.println("usrName:"+userName);
        System.out.println("usrPassword:"+userPassword);

        //通过用户名去数据库查询数据并判断输入是否正确
        User user=userService.getUser(userName);
        if(user==null){
            throw new UnknownAccountException("账号不存在!");
        }else if(!user.getUsrPassword().equals(userPassword)){
            throw new IncorrectCredentialsException("密码不正确!");
        }

        //认证信息
        SimpleAuthenticationInfo authorizationInfo=new SimpleAuthenticationInfo(user,user.getUsrPassword(),getName());
        return authorizationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

5、编写AddPrincipalToSessionFilter配置类(用于解决session丢失)

package com.demo.rememberme.config;


import com.demo.rememberme.entity.User;
import com.demo.rememberme.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 解决session丢失
 */
public class AddPrincipalToSessionFilter extends OncePerRequestFilter {
    @Autowired
    UserService userService;

    @Override
    protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        //查询当前用户的信息
        Subject subject = SecurityUtils.getSubject();
        //判断用户是不是通过自动登录进来的
        if (subject.isRemembered()) {
            User user2=(User)subject.getPrincipal();
            String userName = user2.getUsrName();
            System.out.println(userName+"..........");
            if(userName==null){
                return;
            }
            //根据用户名查询该用户的信息
            User user=userService.getUser(userName);
            if (user == null) return;
            //由于是继承的OncePerRequestFilter,没办法设置session
            //这里发现可以将servletReques强转为子类,所以使用request.getsiion())
            HttpServletRequest request=(HttpServletRequest) servletRequest;
            HttpSession session=request.getSession();
            //当session为空的时候
            if (session.getAttribute("user")==null){
                //把查询到的用户信息设置为session,时效为3600秒
                session.setAttribute("user",user);
                session.setMaxInactiveInterval(3600);
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

6、编写ShiroConfig配置类

package com.demo.rememberme.config;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.OncePerRequestFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    /**
     * cookie管理对象
     * @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;
    }

    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
        //setcookie()的第七个参数
        //设为true后,只能通过http访问,javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //记住我cookie生效时间 ,单位秒
        simpleCookie.setMaxAge(30);
        return simpleCookie;
    }

    @Bean
    public SecurityManager securityManager(){
        System.out.println("1:securityManager..........");
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        System.out.println("2:myShiroRealm..........");
        MyShiroRealm myShiroRealm=new MyShiroRealm();
        return myShiroRealm;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        System.out.println("3:ShiroConfiguration.shiroFilter():配置权限控制规则");
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //登录提交地址
        shiroFilterFactoryBean.setLoginUrl("/login");
        //访问没有授权的资源
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //拦截器
        Map<String,String> filtrChainDefinitionMap=new LinkedHashMap<String,String>();
        //匿名可以访问的地址
        filtrChainDefinitionMap.put("/dologin","anon");
        filtrChainDefinitionMap.put("/css/**","anon");
        filtrChainDefinitionMap.put("/fonts/**","anon");
        filtrChainDefinitionMap.put("/images/**","anon");
        filtrChainDefinitionMap.put("/js/**","anon");
        filtrChainDefinitionMap.put("/localcss/**","anon");
        //配置退出(记住我状态下,可清除记住我的cookie)
        filtrChainDefinitionMap.put("/logout","logout");

        //所有路径必须授权访问(登录),且必须放在最后
        filtrChainDefinitionMap.put("/**","user");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filtrChainDefinitionMap);

        //解决session丢失
        Map<String, Filter> fmap = new HashMap<>();
        fmap.put("addPrincipal", addPrincipalToSessionFilter());
        shiroFilterFactoryBean.setFilters(fmap);

        return shiroFilterFactoryBean;
    }

    /**
     * Shiro自定义过滤器(解决session丢失)
     * @return
     */
    @Bean
    public OncePerRequestFilter addPrincipalToSessionFilter() {
        return new AddPrincipalToSessionFilter();
    }
}

7、编写控制器登录方法

@RequestMapping(value = "/dologin")
    public String login(boolean rememberMe,String usrName, String usrPassword, Map<String,Object> map, HttpSession session, HttpServletRequest request){
        System.out.println("dologin..........");
        try{
            AuthenticationToken token=new UsernamePasswordToken(usrName,usrPassword);
            //设置记住我
            ((UsernamePasswordToken) token).setRememberMe(rememberMe);
            //调用Shiro进行认证
            SecurityUtils.getSubject().login(token);
            //从Shiro中拿出User对象
            User user=(User)SecurityUtils.getSubject().getPrincipal();
            System.out.println("----------"+((UsernamePasswordToken) token).isRememberMe());
            session.setAttribute("user",user);
            System.out.println("登录成功!");
        }catch (Exception e){
            //登录失败,返回登录页面
            map.put("msg",e.getMessage());
            return "login";
        }
        //重定向到另一个控制器请求返回主页面
        return "redirect:/toMain";
    }

8、测试

输入http://localhost:8080进入登录页面
登录页面
勾选记住我后进行登录操作,进入浏览器的检查界面(F12)在Application中找到cookies,这时候就能看到有一条与页面中记住我多选框name相同的记录
登录成功
登录成功后,关闭浏览器,重新打开,并输入需要登录后才能访问的地址,在未勾选记住我时会自动跳转到login页面,而勾选了记住我后则会找到cookie记录并进行自动登录操作
重启浏览器后直接进入

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值