Shiro 可能踩到的那些坑

Shiro 是一款轻量、易用且功能强大的 Java 安全框架。本文记录下自己使用过程中踩到的一些坑

1、记住我

Shiro 开启记住我功能很简单。

首先,注册一个 SimpleCookie 类

@Bean
public SimpleCookie rememberMeCookie() {
    // Cookie名称
    SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
    // 设为true后,只能通过http访问,javascript无法访问
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    // 单位:秒
    simpleCookie.setMaxAge(2592000);
    return simpleCookie;
}

然后把它加入 CookieRememberMeManager 中

@Bean
public CookieRememberMeManager cookieRememberMeManager() {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSAK8DFAdag=="));
    return cookieRememberMeManager;
}

最后在登陆时,根据前台传过来的数据,选择是否记住我

UsernamePasswordToken token = new UsernamePasswordToken(username,password);
if (rememberMe.equals(isRememberMe)){
    token.setRememberMe(true);
}else {
    token.setRememberMe(false);
}

记住我后,你可以在浏览器中找到名为 rememberMe 的 cookie

只要三步,我们就开启了记住我功能,是不是很简单?

但是,在测试的时候你会发现,即使你选择了记住我(cookie 也存在),下次访问时还是要登录

虽然有 rememberMe ,但 Shiro 也会觉得不可靠,那要怎么办呢?

所以我们需要修改拦截路径的级别,将

filterChainMap.put("/**", "authc");

修改为:

filterChainMap.put("/**", "user");

这样就可以正常使用记住我了。不过,敏感页面不建议设置成 "user" 级别。

2、保存 SessionId 的 Cookie 名称

该名称默认为 JSESSIONID,可能会与其他容器的默认名冲突,修改也很简单

@Bean("sessionIdCookie")
public SimpleCookie sessionIdCookie(){
    //这个参数是cookie的名称
    SimpleCookie simpleCookie = new SimpleCookie("sid");
    simpleCookie.setHttpOnly(true);
    simpleCookie.setPath("/");
    //maxAge=-1表示浏览器关闭时失效此Cookie
    simpleCookie.setMaxAge(-1);
    return simpleCookie;
}
@Bean("sessionManager")
public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    //配置监听
    sessionManager.setSessionIdCookie(sessionIdCookie());
    //是否开启删除无效的session对象  默认为true
    sessionManager.setDeleteInvalidSessions(true);
    //取消url 后面的 JSESSIONID
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

3、修改用户信息

在与用户相关的操作中,我们会经常使用

SecurityUtils.getSubject().getPrincipal())

来获取用户信息

但如果我们修改了用户信息(数据库已更新),subject 却还是旧信息

此时,我们需要换一个信息认证给安全管理器

Subject subject = SecurityUtils.getSubject();
PrincipalCollection principalCollection = subject.getPrincipals();
// 你自定义的 Realm
String realmName = principalCollection.getRealmNames().iterator().next();
// 新的信息认证
PrincipalCollection newPrincipalCollection =
        new SimplePrincipalCollection(userInfo, realmName);
subject.runAs(newPrincipalCollection);

这样就完成了用户信息的更新。

不过还有一个问题,此时 session 中的信息还是旧的,如果用户不重新登录或者 session 过期,其信息依然是旧的。

在用户认证时,可以将不可变的信息存入,比如用户名(假设是不可变的),将

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, password, ByteSource.Util.bytes(salt), getName());

替换为(user 对象替换 user.username)

SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), getName());

从 subject 中获取用户名,再用用户名查询数据库获得用户信息,也就不用切换信息认证了。

  • 用支付宝打我
  • 用微信打我

Long may the sunshine

发表评论

电子邮件地址不会被公开。 必填项已用*标注

召唤蕾姆