这是一个困扰了我一周的问题,几乎找遍了全网,也没有找到合适的解决办法,问题的难度在于,不使用任何缓存技术,实现以下问题。
1、当用户登录时,使用shiro rememberme,加密保存cookie 到本地。
2、当用户再次登录时是自动登录状态,但是这时候是没有session的,这里就要解决session的问题。
这里用到的是idea项目:springboot+shiro
文件目录:
让我们从自动登录开始:
一、配置shiroconfig
//cookie
@Bean
public SimpleCookie rememberMeCookie(){
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//只能通过http访问cookie,js不能
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(2678400);
return simpleCookie;
}
/**
* cookie管理对象;
* rememberMeManager()方法是生成rememberMe管理器,而且要将这个rememberMe管理器设置到securityManager中
* @return
*/
@Bean("emanager")
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的** 建议每个项目都不一样 默认AES算法 **长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
//桥梁---这里只添加下面配置记住我这句
@Bean("manager")
public SecurityManager securityManager(@Qualifier("realm") MyRealm realm, @Qualifier("emanager")RememberMeManager emanager){
DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
....
//配置记住我
manager.setRememberMeManager(emanager);
return manager;
}
//进行全局验证配置
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirofilter(@Qualifier("manager") SecurityManager manager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//配置记住我
bean.setSecurityManager(manager);
....
return bean;
}
二、controller,这里的时候要注意,一般来讲,“自动登录、记住密码、七天免登录”这是用户选择的,这里由于业务需要,直接写死,每个用户都要自动登录,所以在下面的行代码中,最后一个直接给了true,如果你需要用户选择,请在前台设置复选框,在参数中接收用户的选择,像第二行代码这样,第三个参数用来接收用户选择。
//第一种:写死
UsernamePasswordToken token=new UsernamePasswordToken(phone, password,true);
//第二种:前台传值
<input type="checkbox" name="isRememberMe" value="1" /> 记住我
public ModelAndView tologin(String phone, String password,@RequestParam(value="isRememberMe", defaultValue="0") Integer isRememberMe){
UsernamePasswordToken token=new UsernamePasswordToken(phone, password);
if (isRememberMe == 1) {
token.setRememberMe(true);
}
}
到此,记住我功能(自动登录功能)已经完成了,接下来是解决session失效的问题,思路:当用户是自动状态登录的时候,我们写一个拦截器,拦截它,获取cookie中的数据,然后到数据库中查询,再重新设置session。
下面给出代码,新建一个类
package com.lynu.esmarket.config;
import com.lynu.esmarket.bean.Login;
import com.lynu.esmarket.service.LoginService;
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;
/**
* Created by jia on 19-4-15.
*/
public class AddPrincipalToSessionFilter extends OncePerRequestFilter {
@Autowired
LoginService ls;
@Override
protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
//查询当前用户的信息
Subject subject = SecurityUtils.getSubject();
//判断用户是不是通过自动登录进来的
if (subject.isRemembered()) {
//如果是,则获取它的手机号(我的登录是以手机号+密码登录的)
String phone = subject.getPrincipal().toString();
if(phone==null){
return;
}
//根据手机号查询该用户的信息
Login login = ls.queryuser(phone);
if (login == null) return;
//由于是继承的OncePerRequestFilter,没办法设置session
//这里发现可以将servletReques强转为子类,所以使用request.getsiion())
HttpServletRequest request=(HttpServletRequest) servletRequest;
HttpSession session=request.getSession();
//当session为空的时候
if (session.getAttribute("login")==null){
//把查询到的用户信息设置为session,时效为3600秒
session.setAttribute("login",login);
session.setMaxInactiveInterval(3600);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
设置shiroconfig
//在shiroconfig中设置
//Shiro自定义过滤器,注意写在@Bean("shiroFilter")的上边
@Bean
public OncePerRequestFilter addPrincipalToSessionFilter() {
return new AddPrincipalToSessionFilter();
}
//在@Bean("shiroFilter")中配置如下
@Bean("shiroFilter")
public ShiroFilterFactoryBean shirofilter(@Qualifier("manager") SecurityManager manager){
//shiro对象
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//配置记住我
bean.setSecurityManager(manager);
//配置刚刚写的拦截器
Map<String, Filter> fmap = new HashMap<>();
fmap.put("addPrincipal", addPrincipalToSessionFilter());
bean.setFilters(fmap);
//所有是user能够访问的都要进行过滤,注意:这个user是shiro中内置的,在remember状态下
//所有自动登录的用户都是user状态
map.put("/**","addPrincipal,user");//需要进行权限验证
其他的就省略了....
bean.setFilterChainDefinitionMap(map);
return bean;
}