Spring Boot中Shiro 1.4.0 Starter如何在Thymeleaf获取PrincipalCollection数据
嘿,我来帮你搞定这个问题~首先明确说:你把轻量用户数据存进Shiro PrincipalCollection的方案完全可行,而且是Shiro设计中推荐的身份信息存储方式,没必要额外自己存会话(当然备选方案也能用,但有点多此一举)。下面给你详细说怎么实现:
一、当前方案的可行性与实现步骤
你用HashMap存储用户信息并加入Principals的做法没问题,Shiro允许在PrincipalCollection中存放任意可序列化的自定义数据(你的HashMap本身是可序列化的,完全符合要求)。要在Thymeleaf里拿到这些数据,你可以借助Shiro和Thymeleaf的集成能力:
1. 确保引入Shiro-Thymeleaf依赖
如果还没加,在你的pom.xml(Maven)里添加:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-thymeleaf</artifactId> <version>1.4.0</version> </dependency>
Gradle项目对应添加依赖即可。
2. 在Thymeleaf模板中直接获取数据
Shiro的Thymeleaf集成会自动把当前Subject对象放到模板上下文里,你可以通过它直接提取PrincipalCollection中的HashMap:
<p th:text="#{welcome.lastlogin.message(${subject.principals.oneByType(T(java.util.HashMap)).firstName},${subject.principals.oneByType(T(java.util.HashMap)).lastLoginTime})}">Hello, user, how are you today?</p>
这里T(java.util.HashMap)是Thymeleaf中引用Java类的语法,oneByType方法会从PrincipalCollection里找出第一个HashMap类型的对象,也就是你存的用户数据。
优化建议:用自定义对象替代HashMap
虽然HashMap能用,但更推荐把用户数据封装成一个自定义的UserInfo类(比如包含userId、firstName、lastLoginTime等字段),这样类型更明确,避免模板里的类型转换问题,代码可读性也更高:
public class UserInfo implements Serializable { private Integer userId; private String firstName; private Date lastLoginTime; // 构造器、getter/setter方法 }
然后在UserRealm的认证方法中,把UserInfo对象加入PrincipalCollection:
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 省略认证逻辑... UserInfo userInfo = new UserInfo(1, "John", new Date()); return new SimpleAuthenticationInfo(userInfo, credentials, getName()); }
之后模板里的表达式就更简洁了:
<p th:text="#{welcome.lastlogin.message(${subject.principals.oneByType(com.yourpackage.UserInfo).firstName},${subject.principals.oneByType(com.yourpackage.UserInfo).lastLoginTime})}">Hello, user, how are you today?</p>
二、关于备选方案:自行存入会话
这个方案确实可行,但并不推荐——因为Shiro的PrincipalCollection本身就是用来绑定当前用户身份信息的,已经自动同步到会话中(你看到的org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY就是Shiro自动存储的键)。自己存会话的话,还要额外处理序列化、会话同步、数据一致性等问题,反而增加了不必要的复杂度,不如直接用Shiro原生的机制更省心。
额外小技巧:通过Controller传递数据到模板
如果觉得模板里的表达式太长,也可以在Controller中先提取用户数据,放到Model里再传递给模板:
@GetMapping("/index") public String index(Model model) { Subject subject = SecurityUtils.getSubject(); // 这里如果是自定义UserInfo类,就换成UserInfo.class Map<String, Object> userData = (Map<String, Object>) subject.getPrincipals().oneByType(Map.class); model.addAttribute("currentUser", userData); return "index"; }
然后模板里直接用:
<p th:text="#{welcome.lastlogin.message(${currentUser.firstName},${currentUser.lastLoginTime})}">Hello, user, how are you today?</p>
这样模板的表达式更简洁,也更易维护。
内容的提问来源于stack exchange,提问作者Stego

