我正在创建一个应用程序,我必须使用 Spring Security登录 。这是标准login/logout
,我发现了很多教程如何创建它。什么不标准 - 是数据库中的表角色。我无法更改数据库,我可以使用它。我为用户和角色制作了正确的实体,但我无法理解,如何使用UserDetailsServiceImpl
正确编写loadUserByUsername
。我甚至找不到一些亲密的东西......
实体:
@Entity
@Table(name = "user")
public class User implements model.Entity {
@Id
@GeneratedValue
@Column(name = "userId", nullable = false)
private int userId;
@Column(name = "firstName")
private String firstName;
@Column(name = "lastName")
private String lastName;
@Column(name = "login", nullable = false)
private String login;
@Column(name = "password", nullable = false)
private String password;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "roleId", nullable = false)
private Set<Role> roleId;
@Transient
private String confirmPassword;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
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 String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Set<Role> getRoleId() {
return roleId;
}
public void setRoleId(Set<Role> roleId) {
this.roleId = roleId;
}
}
作用:
@Entity
@Table(name = "role")
public class Role implements model.Entity {
@Id
@GeneratedValue
@Column(name = "roleId", nullable = false)
private int roleId;
@Column(name = "user")
private boolean user;
@Column(name = "tutor")
private boolean tutor;
@Column(name = "admin")
private boolean admin;
public Role() {} // Empty constructor to have POJO class
public int getRoleId() {
return roleId;
}
public void setRoleId(int roleId) {
this.roleId = roleId;
}
public boolean isUser() {
return user;
}
public void setUser(boolean user) {
this.user = user;
}
public boolean isTutor() {
return tutor;
}
public void setTutor(boolean tutor) {
this.tutor = tutor;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
@Override
public String toString() {
return "Role{" +
"roleId=" + roleId +
", user='" + user + '\'' +
", tutor=" + tutor + '\'' +
", admin=" + admin +
'}';
}
}
所以主要的问题是如何创建实现UserDetailsService的UserDetailServiceImpl的实现:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
...
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
...
return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), grantedAuthorities);
}
也许我应该创建特殊的类,它返回用户的确切角色..或者可能还有其他方法?
我不要求为我编码,只是帮我说如何让它更好地实现这样的角色。主要目标是划分Admin
,Tutor
和User
。
答案 0 :(得分:0)
我做了类似的事情,尽管我的用户只能有一个角色。
@Override
@Transactional
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
final User user = Optional.ofNullable(findUserByEmail(username)).orElseThrow(() -> new UsernameNotFoundException("User was not found"));
List<GrantedAuthority> authorities = getUserAuthority(user.getRole());
final boolean canLogin = user.isActive() && user.isVerified();
if (!canLogin) {
throw new AccountException("User is not enabled");
}
return buildUserForAuthentication(user, authorities);
}
private List<GrantedAuthority> getUserAuthority(final Role userRole) {
return Collections.singletonList(new SimpleGrantedAuthority(userRole.getRole()));
}
private UserDetails buildUserForAuthentication(final User user, final List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
true,
true,
true,
true,
authorities);
}
您可以将getUserAuthority改编为类似于:
的内容 private List<GrantedAuthority> getUserAuthorities(final Set<Role> roles) {
return roles.stream().map(roleId -> {
final Role role = roleRepository.findOne(roleId);
if (role.isAdmin) {
return new SimpleGrantedAuthority("ROLE_ADMIN");
} else if (role.isUser) {
return new SimpleGrantedAuthority("ROLE_USER");
}
// like wise for all your roles.
}).collect(Collectors.toList());
}
答案 1 :(得分:0)
鉴于我在某种程度上同意holmis83评论,因为实际上可能存在role
表在某些组合中可能具有奇怪(或至少,重复甚至相互矛盾)信息的某些情况,有几个你可以采取的方式。
首先,我建议你在数据库中创建一个视图来处理role
表格,使其更加authorities-by-username-query
友好
我会这样做:
SELECT roleId, 'ROLE_USER' as role FROM role WHERE user = 1
UNION
SELECT roleId, 'ROLE_TUTOR' as role FROM role WHERE tutor = 1
UNION
SELECT roleId, 'ROLE_ADMIN' as role FROM role WHERE admin = 1;
就这样,对于像这样的数据库模型
你会得到这样的结果:
现在,您可以使用新创建的视图而不是原始表格使用authorities-by-username-query
用户inner join
。
SELECT user.login, roles_view.role FROM user as user
INNER JOIN user_has_role as user_role ON user.userId = user_role.user_userId
INNER JOIN roles_view ON user_role.role_roleId = roles_view.roleId
这将是输出:
username | role
----------------------
jlumietu | ROLE_USER
jlumietu | ROLE_ADMIN
username | ROLE_USER
username | ROLE_TUTOR
username | ROLE_ADMIN
username | ROLE_ADMIN
username | ROLE_USER
username | ROLE_TUTOR
username | ROLE_ADMIN
username | ROLE_TUTOR
由于可能存在重复信息,您可以使用用户名和角色成为一个群组,就这样:
SELECT user.login, roles_view.role FROM user
INNER JOIN user_has_role as user_role ON user.userId = user_role.user_userId
INNER JOIN roles_view
ON user_role.role_roleId = roles_view.roleId
GROUP BY login, role;
只是为了得到这个结果:
username | role
----------------------
jlumietu | ROLE_ADMIN
jlumietu | ROLE_USER
username | ROLE_ADMIN
username | ROLE_TUTOR
username | ROLE_USER
事实上,没有必要这样做,因为Spring安全会处理它以避免重复的角色,但为了便于阅读,如果查询是手动执行的,我认为这是值得的。
说完这一切之后,让我们检查一下安全配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<security:http use-expressions="true" authentication-manager-ref="authenticationManager">
<security:intercept-url pattern="/simple/**" access="permitAll()" />
<security:intercept-url pattern="/secured/**" access="isAuthenticated()" />
<security:form-login
login-page="/simple/login.htm"
authentication-failure-url="/simple/login.htm?error=true"
default-target-url="/secured/home.htm"
username-parameter="email"
password-parameter="password"
login-processing-url="/secured/performLogin.htm" />
<security:logout
logout-url="/secured/performLogout.htm"
logout-success-url="/simple/login.htm" />
<security:csrf disabled="true" />
</security:http>
<security:authentication-manager id="authenticationManager">
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:jdbc-user-service id="jdbcUserService" data-source-ref="dataSource"
users-by-username-query="
SELECT login AS username, password AS password, '1' AS enabled
FROM user
WHERE user.login=?"
authorities-by-username-query="
SELECT user.login, roles_view.role
FROM user
INNER JOIN user_has_role as user_role ON user.userId = user_role.user_userId
INNER JOIN roles_view ON user_role.role_roleId = roles_view.roleId
where user.login = ?
GROUP BY login, role"
/>
</security:authentication-provider>
</security:authentication-manager>
</beans:beans>
即使您无法在数据库中创建视图,只需在role
中的authorities-by-username query
表中键入select-union sql,您就可以设置它。
请注意,通过此解决方法,您甚至无需编写自定义的UserDetailsService