Spring Boot中User的authorities字段视图传递后为空的排查与解决
我维护一个基于Spring Boot、Spring MVC和Spring Security的项目,项目支持从数据库获取User对象并在指定视图中执行部分更新操作,比如用户修改密码的场景:
GET请求处理器(/user/update-password)
@GetMapping("/user/update-password") public String updateYourPassword(Model model, Authentication authentication) { String username = getUsername(authentication); User user = service.getByUsername(username); model.addAttribute("user", user); System.out.println("user in handler: " + user); // LOG #1 return "user-update-password"; }
前端视图(user-update-password)表单代码
<form class="forms" th:action="@{/save-user(passwordChange=true)}" th:object="${user}" method="post"> <input type="hidden" th:field="*{id}"/> <input type="hidden" th:field="*{username}"/> <fieldset> <label for="password">Your new password: </label> <input id="password" name="password" required/> </fieldset> <input type="hidden" th:field="*{name}"/> <input type="hidden" th:field="*{lastName}"/> <input type="hidden" th:field="*{department}"/> <input type="hidden" th:field="*{salary}"/> <input type="hidden" th:field="*{age}"/> <input type="hidden" th:field="*{email}"/> <input type="hidden" th:field="*{enabled}"> <input type="hidden" th:field="*{authorities}"> <input class="main-button" type="submit" value="Submit"> </form>
POST请求处理器(/save-user)
@PostMapping("/save-user") public String saveUser(@ModelAttribute User user, @RequestParam(defaultValue = "false") String passwordChange, Authentication authentication) { System.out.println("User @ModelAttribute: " + user); // LOG #2 if (Boolean.parseBoolean(passwordChange)) { System.out.println("Encoding..."); encodePassword(user); System.out.println("Encoding complete!"); } service.save(user); return isAdmin(authentication) ? "redirect:/admin" : "redirect:/user"; }
问题现象
密码修改操作执行成功,但User对象的authorities字段被设置为null。日志显示GET处理器中的User对象包含authorities=[Role[id=1, role='USER']],但POST处理器通过@ModelAttribute接收的User对象中该字段为null。
实体类定义
User实体
@Entity @Table(name = "users") public class User implements UserDetails { // ... @ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> authorities; }
Role实体
@Entity @Table(name = "roles") public class Role implements GrantedAuthority { // ... @Column(name = "role") private String authority; @ManyToMany @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "role_id"), inverseJoinColumns = @JoinColumn(name = "user_id")) private Set<User> userList; // ... @Override public String toString() { return new StringJoiner(", ", Role.class.getSimpleName() + "[", "]") .add("id=" + id) .add("role='" + authority + "'") .toString(); } }
尝试过的无效方案
一开始尝试自定义String到Set<Role>的Formatter,但未生效:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { // ... @Override public void addFormatters(FormatterRegistry registry) { System.out.println("addFormatters() called"); registry.addFormatter(new Formatter<Set<Role>>() { @Override public Set<Role> parse(String text, Locale locale) { Set<Role> roleSet = new HashSet<>(); String[] roles = text.split("^\\[|\\]$|(?<=\\]),\\s?"); for (String roleString : roles) { String authority = roleString.substring(roleString.lastIndexOf("=") + 1, roleString.indexOf("]") - 1); roleSet.add(service.getRoleByName(authority)); } return roleSet; } @Override public String print(Set<Role> object, Locale locale) { return null; } }); } }
最终解决方案
排查后发现问题出在视图层:authorities是集合类型,集合接口没有重写toString()方法,导致原隐藏字段无法正确输出集合内容。
修改视图的隐藏字段写法
<!-- authorities是集合类型,集合接口没有重写toString()方法 --> <input type="hidden" th:name="authorities" th:value="*{#strings.toString(authorities)}"/>
调整Formatter的解析逻辑
@Override public Set<Role> parse(String text, Locale locale) { Set<Role> roleSet = new HashSet<>(); String[] roles = text.split("^\\[|\\]$|(?<=\\]),\\s?"); for (String roleString : roles) { if (roleString.length() == 0) continue; String authority = roleString.substring(roleString.lastIndexOf("=") + 2, roleString.indexOf("]") - 1); roleSet.add(service.getRoleByName(authority)); } return roleSet; }
通过以上修改,成功解决了authorities字段丢失的问题。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

