You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中User的authorities字段视图传递后为空的排查与解决

Spring Boot用户部分更新时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:54:59