Spring 安全角色设置不正确
Spring Security Roles Not Being Set Properly
我已经从 Spring Security 实现了自己的 User 和 UserDetailsService,因为我需要在用户对象中存储额外的信息 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。
这是我对 UserDetailsService 的实现
@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>
更新:
我将 CustomUserDetailsService 更新为以下内容:
@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]))
我已经从 Spring Security 实现了自己的 User 和 UserDetailsService,因为我需要在用户对象中存储额外的信息 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。
这是我对 UserDetailsService 的实现
@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>
更新:
我将 CustomUserDetailsService 更新为以下内容:
@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]))