0

我目前正在开发一个简单的 SpringMVC 应用程序。我最近开始使用 Spring-security。用户及其各自的角色保存在数据库中。我当前的任务是实现“注册用户”表单。我正在使用一个简单的 .jsp 和 spring 表单。用户可以有 2 个角色:ROLE_USER 和 ROLE_ADMIN,并且默认选中 ROLE_USER。

这是我的 AppUser 和 AppUserRoles 模型。

应用用户.java:

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name="appusers")
public class AppUser {

    @Id
    @Column(name="USERNAME", unique = true, nullable = false, length = 45)
    @NotEmpty(message="Username field cannot be empty")
    private String username;

    @Column(name="PASSWORD", nullable = false, length = 60)
    @NotEmpty(message="Password field cannot be empty")
    @Size(min=6,max=10,message="Password must be between 6 and 10 letters")
    private String password;

    @NotEmpty(message="The user must have at least one defined role")
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "appUser")
    private Set<AppUserRole> userRole = new HashSet<AppUserRole>(0);

    public AppUser() {
        super();
    }


    public AppUser(String username, String password) {
        super();
        this.username = username;
        this.password = password;
    }


    public AppUser(String username, String password, Set<AppUserRole> userRole) {
        super();
        this.username = username;
        this.password = password;
        this.userRole = userRole;
    }

    public String getUsername() {
        return username;
    }


    public void setUsername(String username) {
        this.username = username;
    }


    public String getPassword() {
        return password;
    }


    public void setPassword(String password) {
        this.password = password;
    }


    public Set<AppUserRole> getUserRole() {
        return userRole;
    }


    public void setUserRole(Set<AppUserRole> userRole) {
        this.userRole = userRole;
    }

}

应用用户角色.java

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


@Entity
@Table(name="appuser_roles")
public class AppUserRole {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "USER_ROLE_ID", unique = true, nullable = false)
    private Integer userRoleId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "username", nullable = false)
    private AppUser appUser;

    @Column(name = "ROLE", nullable = false, length = 45)
    private String role;

    public AppUserRole() {
        super();
    }

    public AppUserRole(AppUser appuser, String role) {
        super();
        this.appUser = appuser;
        this.role = role;
    }

    public Integer getUserRoleId() {
        return userRoleId;
    }
    public void setUserRoleId(Integer userRoleId) {
        this.userRoleId = userRoleId;
    }
    public AppUser getAppuser() {
        return appUser;
    }
    public void setAppuser(AppUser appuser) {
        this.appUser = appuser;
    }
    public String getRole() {
        return role;
    }
    public void setRole(String role) {
        this.role = role;
    }

}

包含 registerUser() 方法的 AppUserDAOImpl.java 片段:

@Override
public void registerUser(AppUser appUser) {

    sessionFactory.getCurrentSession().persist(appUser);
    logger.info(appUser.getUsername() + " persisted");
}

与注册表单相关的 UserController.java 片段:

@RequestMapping(value="/register")
public String goRegister(Model model) {
    model.addAttribute("AppUser", new AppUser());
    return "register";
}

@RequestMapping(value= "/registeruser_action", method = RequestMethod.POST)
public String addUser(@ModelAttribute("AppUser") @Valid AppUser appUser, BindingResult result){

    if(result.hasErrors()){
        return "register";
    }

    appUserDetailsService.registerUser(appUser);
    return "redirect:/menu";    
}

register.jsp 片段的实际形式:

    <sf:form commandName="AppUser" action="registeruser_action" method="POST" >

        <div>
            Username:<br>
            <sf:input type="text" path="username"/>
            <sf:errors path="username" cssClass="errors"/>
        </div>
        <div>
            Password<br>
            <sf:input type="text" path="password"/>
            <sf:errors path="password" cssClass="errors"/>
        </div>
        <div>
            User role:<br>
            <sf:checkbox path="userRole" value="ROLE_USER" 
                disabled="true" checked="true" /> User <br>
            <sf:checkbox path="userRole" value="ROLE_ADMIN"/> Admin <br>
            <sf:errors path="userRole" cssClass="errors"/>
        </div>
        <br>

        <input type="submit" value="Submit">

    </sf:form>

具体来说,我想要做什么但似乎无法在互联网上弄清楚或找到:我希望将复选框选择添加到我的 AppUser.java 模型中设置的 userRole 中,然后将它们持久化。问题是,我之前在我的项目中已经将表单输入映射到对象字段,但从未映射到集合。我内心的乐观主义者非常希望我可以通过将复选框映射到 spring 表单中的 userRole 路径来做到这一点,也许 java 和 spring 会“自动”理解它应该将它们添加到该集合中。我内心的现实主义者有点知道这不是那么简单。老实说,我知道这很简单,但我似乎无法找到解决方案。希望在这里找到解决方案。

PS:我只发布了我认为对这个问题很重要的内容。例如,服务层似乎是额外的。将根据需要发布额外的信息/代码。

编辑: 好的,我按照akshay的建议在构造函数中添加了ROLE_USER。尝试注册新用户时产生的异常让我意识到我完全错了。我正在尝试将 String 分配给 Set 对象。userRole 是 Set 类型,ROLE_USER 是一个字符串。该集合包含 AppUserRole 对象,该对象应包含用户名和相应的角色字符串。基本上,现在的问题是:如何将包含用户名和相应角色的新对象直接从 spring 表单添加到 userRole 集中?

4

1 回答 1

0

好的,您需要在这里做一些事情。

假设您要显示所有可用角色的列表:用户检查他想要分配给当前用户的角色。

目前 Role 是一个简单的字符串,因此没有任何东西可以绑定复选框。然后,您可以在视图层上创建一个包装器:

public class RoleWrapper{

private String roleName;
private boolean selected;

}

在您的控制器上,您要添加一个将加载所有可用角色的 ModelAttribute。请注意,您不能绑定到原始集合,因此需要包装此列表。所以创建另一个类来包装你的列表:

public class RoleList{

private List<RoleWrapper> roles;

}

在您的控制器中构造一个实例并设置为模型属性:

@ModelAttribute("availableRoles")
public RoleList getAvailableRoles(){
//load from db and construct list

}

在您的 JSP 中,创建一个由该列表支持的表单,并将复选框绑定到布尔字段:

<form:form modelAttribute="roleList" method="post">
    <table>
        <thead>
            <tr>
                <th style="width: 80%;">Role</th>
                <th>Select</th>
            </tr>
        </thead>
        <tbody>
            <c:forEach items="${roleList.roles}"
                var="role" varStatus="status">
                <tr>
                    <td>role.name</td>
                    <td><form:checkbox
                            path="roles[${status.index}].selected" /></td>
                </tr>
            </c:forEach>
        </tbody>
    </table>
</form>

更新您的控制器以在提交时通过此列表。相应地迭代并设置用户角色:

@RequestMapping(value= "/registeruser_action", method = RequestMethod.POST)
public String addUser(@ModelAttribute("AppUser") @Valid AppUser appUser, @ModelAttribute("roleList") RoleList roleList, BindingResult result){

    if(result.hasErrors()){
        return "register";
    }

    for(RoleWrapper role : roleList.getRoles()){
        if(role.isSelected()){
            //add to user
        }
    }

    appUserDetailsService.registerUser(appUser);


    return "redirect:/menu";    
}

关于您一直存在的问题,您需要执行以下操作。向关系添加级联选项,如果保留非拥有方(在本例中为 AppUser),请确保设置关系的双方。您应该封装添加/删除操作以确保域对象之间的关系是一致的。因此:

@Entity
@Table(name="appusers")
public class AppUser {

    @NotEmpty(message="The user must have at least one defined role")
    @OneToMany(fetch = FetchType.LAZY, cascade = cascadeType.ALL, mappedBy = "appUser")
    private Set<AppUserRole> userRoles = new HashSet<AppUserRole>(0);

    /**
    *   Force clients through add/remove. Plus, do not provide setter.
    */
    public Set<AppUserRole> getUserRoles() {
        return Collections.unmodifiableSet(userRoles);
    }

    public void addRole(AppUserRole userRole){
        userRole.setAppUser(this); //both sides set
        userRoles.add(userRole);
    }

    public void removeRole(AppUserRole userRole){
        userRoles.remove(userRole);
    }
}
于 2014-11-27T16:20:49.703 回答