Spring Security - UserDetailsService 实现 - 登录失败
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14268451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Security - UserDetailsService implementation - Login Fails
提问by Daniele Grigioni
I'm quite new to spring and i'having this issue with spring security. Actually it only works without my custom UserDetailsService implementation.
我对 spring 很陌生,我在 spring 安全方面遇到了这个问题。实际上它只有在没有我的自定义 UserDetailsService 实现的情况下才有效。
Account and Role Objects
帐户和角色对象
@Entity
@Table(name="ACCOUNT", uniqueConstraints = {@UniqueConstraint (columnNames = "USERNAME"), @UniqueConstraint (columnNames = "EMAIL")})
public class Account implements Serializable {
private static final long serialVersionUID = 2872791921224905344L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="ID")
private Integer id;
@Column(name="USERNAME")
@NotNull
private String username;
@Column(name="PASSWORD")
@NotNull
private String password;
@Column(name="EMAIL")
@NotNull
@Email
private String email;
@Column(name="ENABLED")
private boolean enabled;
@ManyToMany(cascade= CascadeType.ALL)
@JoinTable(name="ACCOUNT_ROLE", joinColumns = {@JoinColumn (name="ID_ACCOUNT")}, inverseJoinColumns ={ @JoinColumn (name="ID_ROLE")})
private Set<Role> roles = new HashSet<Role>(0);
Role
角色
@Entity
@Table(name="ROLE", uniqueConstraints={@UniqueConstraint (columnNames="NAME")})
public class Role implements Serializable{
private static final long serialVersionUID = -9162292216387180496L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "NAME")
@NotNull
private String name;
@ManyToMany(mappedBy = "roles")
private Set<Account> accounts = new HashSet<Account>(0);
The adapter for the UserDetails
UserDetails 的适配器
@SuppressWarnings({ "serial", "deprecation" })
public class UserDetailsAdapter implements UserDetails {
private Account account;
public UserDetailsAdapter(Account account) {this.account = account;}
public Account getAccount() {return account;}
public Integer getId(){return account.getId();}
public String getEmail () {return account.getEmail();}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for (Role r :account.getRoles()) {
authorities.add(new GrantedAuthorityImpl(r.getName()));
}
return authorities;
}
The custom UserDetailsService
自定义 UserDetailsService
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Inject AccountDao accountDao;
@Inject RoleDao roleDao;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Account account= accountDao.findByUsername(username);
if(account==null) {throw new UsernameNotFoundException("No such user: " + username);
} else if (account.getRoles().isEmpty()) {
throw new UsernameNotFoundException("User " + username + " has no authorities");
}
UserDetailsAdapter user = new UserDetailsAdapter(account);
return user;
}
The web.xml
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml
/WEB-INF/spring/appServlet/security- context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Creates the Filters to handle hibernate lazyload exception -->
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
the root-context
根上下文
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.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-3.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:property-placeholder properties-ref="deployProperties"/>
<!-- Remember to correctly locate the right file for properties configuration(example DB connection parameters) -->
<bean id="deployProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/spring/appServlet/spring.properties" />
<context:annotation-config/>
<context:component-scan base-package="org.treci">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<import resource="/appServlet/data-context.xml"/>
The security context
安全上下文
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<http pattern="/resources" security="none" />
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/" access="permitAll"/>
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/login-success" access="hasRole('ROLE_ADMIN')"/>
<form-login login-page="/login"
default-target-url="/login-success"
authentication-failure-url="/login-failed"/>
<logout logout-success-url="/logout"/>
</http>
<beans:bean id="customUserDetailsService" class="org.treci.app.service.CustomUserDetailsService"></beans:bean>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService">
</authentication-provider>
</authentication-manager>
</beans:beans>
I hope some of you can help, save me :)
我希望你们中的一些人可以帮助,救救我:)
回答by Daniele Grigioni
Here's how I solved the problem:
这是我解决问题的方法:
in the CustomDetailsService I was returning a UserDetails Object by using an Adapter as you can see.
如您所见,在 CustomDetailsService 中,我使用适配器返回了一个 UserDetails 对象。
Looking at some tutorial I realized that i should return an org.springframework.security.core.userdetails.User Object.
看着一些教程,我意识到我应该返回一个 org.springframework.security.core.userdetails.User 对象。
Here's my new CustomDetailsService implementation:
这是我的新 CustomDetailsService 实现:
@Transactional(readOnly=true)
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
@Inject AccountDao accountDao;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Account account= accountDao.findByUsername(username);
if(account==null) {throw new UsernameNotFoundException("No such user: " + username);
} else if (account.getRoles().isEmpty()) {
throw new UsernameNotFoundException("User " + username + " has no authorities");
}
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
return new User(
account.getUsername(),
account.getPassword().toLowerCase(),
account.isEnabled(),
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(account.getRoles()));
}
public List<String> getRolesAsList(Set<Role> roles) {
List <String> rolesAsList = new ArrayList<String>();
for(Role role : roles){
rolesAsList.add(role.getName());
}
return rolesAsList;
}
public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
public Collection<? extends GrantedAuthority> getAuthorities(Set<Role> roles) {
List<GrantedAuthority> authList = getGrantedAuthorities(getRolesAsList(roles));
return authList;
}
}

