Spring 安全角色设置不正确

Spring Security Roles Not Being Set Properly

我已经从 Spring Security 实现了自己的 User 和 UserDetailsS​​ervice,因为我需要在用户对象中存储额外的信息 Spring Security holds。但是我认为角色设置不正确。在数据库和应用程序中,一个用户可以拥有 0、1 或多个角色。

我的 thymeleaf 视图中有以下块

<!-- header / navigation based on role of the user-->
    <th:block sec:authorize="hasRole('ROLE_ADMIN')">
        <header th:include="fragments/header :: headerAdmin"></header>
    </th:block>
    <th:block sec:authorize="hasRole('ROLE_USER')">
        <header th:include="fragments/header :: headerUser"></header>
    </th:block>
    <th:block sec:authorize="isAnonymous()">
        <header th:include="fragments/header :: headerPublic"></header>
    </th:block>

一旦我登录 none 那些节目,但我至少在数据库中有 ROLE_ADMIN。

这是我对 UserDetailsS​​ervice 的实现

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);

    @Autowired
    private UserMapper userMapper;

    @Override
    public CustomUser loadUserByUsername(String email) throws UsernameNotFoundException {

        logger.info("Authenticating user with email {}", email);
        User user = userMapper.getUserByEmail(email);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("User with email %s was not found", email));
        }
        return new CustomUser(user);
    }
}

还有我的用户实现

public class CustomUser extends org.springframework.security.core.userdetails.User {

    private User user;

    public CustomUser(User user) {
        super(user.getEmail(),user.getPassword(), AuthorityUtils.createAuthorityList(user.getRoles().toString()));
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Collection<String> getRoles() {
        return this.user.getRoles();
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("user", user)
                .toString();
    }
}

我将这个块添加到我的控制器中

@Controller
public class HomeController {

    private static Logger logger = LoggerFactory.getLogger(HomeController.class);

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String getHome() {
        Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>)    SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        logger.info(authorities.toString());
        return "home/index";
    }
}

它打印了以下内容:

16:30:04.920 INFO  HomeController.getHome - [[ROLE_ADMIN]]

作为参考,这是 MyBatis 正在填充的用户域对象

public class User implements Serializable {

    private long id;
    private String email;
    private String password;
    private String firstName;
    private String lastName;
    private long institutionId;
    private char locked;
    private Date createdAt;
    private Date lastLogin;
    private int failedLoginAttempts;
    private Collection<String> roles;

    public User() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public long getInstitutionId() {
        return institutionId;
    }

    public void setInstitutionId(long institutionId) {
        this.institutionId = institutionId;
    }

    public char getLocked() {
        return locked;
    }

    public void setLocked(char locked) {
        this.locked = locked;
    }

    public Collection<String> getRoles() {
        return roles;
    }

    public void setRoles(Collection<String> roles) {
        this.roles = roles;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getLastLogin() {
        return lastLogin;
    }

    public void setLastLogin(Date lastLogin) {
        this.lastLogin = lastLogin;
    }

    public int getFailedLoginAttempts() {
        return failedLoginAttempts;
    }

    public void setFailedLoginAttempts(int failedLoginAttempts) {
        this.failedLoginAttempts = failedLoginAttempts;
    }
}

还有 MyBatis 映射器

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.ohtech.innovationexchange.persistence.UserMapper">

    <resultMap id="userMap" type="org.ohtech.innovationexchange.domain.User">
        <id column="ID" property="id" />
        <id column="EMAIL" property="email" />
        <id column="PASSWORD" property="password" />
        <id column="FIRST_NAME" property="firstName" />
        <id column="LAST_NAME" property="lastName" />
        <id column="INSTITUTION_ID" property="institutionId" />
        <id column="LOCKED" property="locked" />
        <id column="CREATED_AT" property="createdAt" />
        <id column="LAST_LOGIN" property="lastLogin" />
        <id column="FAILED_LOGIN_ATTEMPTS" property="failedLoginAttempts" />
        <collection property="roles" ofType="String">
            <id column="SPRING_NAME" />
        </collection>
    </resultMap>

    <select id="getUserByEmail" statementType="PREPARED" parameterType="String" resultMap="userMap">
        SELECT
          u.ID,
          u.EMAIL,
          u.PASSWORD,
          u.FIRST_NAME,
          u.LAST_NAME,
          u.INSTITUTION_ID,
          u.LOCKED,
          u.CREATED_AT,
          u.LAST_LOGIN,
          u.FAILED_LOGIN_ATTEMPTS,
          r.SPRING_NAME
        FROM T_USER u
        INNER JOIN T_USER_ROLE tr
          ON u.ID = tr.USER_ID
        INNER JOIN T_ROLE r
          ON tr.ROLE_ID = r.ID
        WHERE EMAIL = #{email}
    </select>
</mapper>

更新:

我将 CustomUserDetailsS​​ervice 更新为以下内容:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);

    @Autowired
    private UserMapper userMapper;

    @Override
    public CustomUser loadUserByUsername(String email) throws UsernameNotFoundException {

        logger.info("Authenticating user with email {}", email);
        User user = userMapper.getUserByEmail(email);
        if (user == null) {
            throw new UsernameNotFoundException(String.format("User with email %s was not found", email));
        }

        Collection<GrantedAuthority> authorities = new HashSet<>();
        for (String role : user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role));
        }

        return new CustomUser(user, authorities);
    }
}

和 CustomUser 到:

public class CustomUser extends org.springframework.security.core.userdetails.User {

    private User user;

    public CustomUser(User user, Collection<GrantedAuthority> grantedAuthorities) {
        super(user.getEmail(),user.getPassword(), grantedAuthorities);
        this.user = user;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Collection<String> getRoles() {
        return this.user.getRoles();
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this)
                .add("user", user)
                .toString();
    }
}

正在打印:

18:02:29.045 INFO  HomeController.getHome - [ROLE_ADMIN, ROLE_INSTITUTION_USER]

但我的 Thymeleaf sec 块仍然无法正常工作。

这是错误的:

AuthorityUtils.createAuthorityList(user.getRoles().toString())

因为createAuthorityList 不会解析 Collection 的字符串表示形式。相反,你应该使用类似的东西:

AuthorityUtils.createAuthorityList(user.getRoles().toArray(new String[0]))