Ich muss einige zusätzliche Informationen über den Benutzer speichern, um übermäßige Datenbankanforderungen auszuschließen. So wie ich es verstanden habe, muss ich:
1) Override UserDetails Schnittstelle. In dieser Implementierung speichern Sie alle zusätzlichen Informationen, die ich brauche.
2) Überschreiben Sie die UserDetailsService-Schnittstelle. In loadUserByUsername (String email) muss ich meine Implementierung der UserDetails Klasse zurückgeben. Danach kann ich dieses Objekt mit der getPrincipal()
Methode bekommen.Wie behebt man die Implementierung von ClassCastException for UserDetails?
Bitte korrigieren Sie mich, wenn ich einen Fehler in 1) oder 2) gemacht habe. Also welches Problem habe ich? Eigentlich, wenn ich versuche, getPrincipal()
gibt es org.springframework.security.core.userdetails.User
Implementierung der UserDetails-Schnittstelle, die ich nicht in der Lage bin zu LoggedUser zu werfen, weil, logisch, dass ClassCastException
auftritt.
Vielen Dank im Voraus für jede Hilfe. P.S. Ich habe auch versucht, Benutzerklasse zu erweitern, hatte aber das gleiche Ergebnis.
AuthUserDetailsService (UserDetailsService Implementierung):
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
public Storages storage;
@Override
public LoggedUser loadUserByUsername(String email)
throws UsernameNotFoundException {
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
milkiv.easyword.models.User user = getUserDetails(email);
if (user != null) {
LoggedUser userdetails = new LoggedUser(
user,
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(1)
);
return userdetails;
} else {
throw new UsernameNotFoundException(email);
}
}
public List<GrantedAuthority> getAuthorities(int role) {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
if (role == 1) {
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else if (role == 2) {
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return authList;
}
private milkiv.easyword.models.User getUserDetails(String email) {
milkiv.easyword.models.User user = storage.uSM.findByEmail(email);
return user;
}
}
LoggedUser (Userdetails Implementierung):
public class LoggedUser implements UserDetails{
User user = new User();
private boolean enabled;
private boolean accountNonExpired;
private boolean credentialsNonExpired;
private boolean accountNonLocked;
private Collection<? extends GrantedAuthority> authorities;
public LoggedUser() {
}
public LoggedUser(User user, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
setUser(user);
this.enabled=enabled;
this.accountNonExpired=accountNonExpired;
this.credentialsNonExpired=credentialsNonExpired;
this.accountNonLocked=accountNonLocked;
this.authorities=authorities;
}
public void setUser(User user) {
this.user = user;
}
private User getUser() {
return this.user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.user.getPassword();
}
@Override
public String getUsername() {
return this.user.getNickname();
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enabled;
}
public String getEmail(){
return this.user.getEmail();
}
public int getId(){
return this.user.getUserId();
}
public Date getRegistrationDate(){
return this.user.getRegistrationDate();
}
}
feder security.xml
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/sign/in" access="isAnonymous()" />
<intercept-url pattern="/sign/up" access="isAnonymous()" />
<intercept-url pattern="/secret/page" access="isAuthenticated()" />
<intercept-url pattern="/sign/out" access="isAuthenticated()" />
<intercept-url pattern="/user/myinfo" access="isAuthenticated()" />
<form-login
login-page="/sign/in"
default-target-url="/secret/page"
authentication-failure-url="/sign/in?failed=1"
password-parameter="password"
username-parameter="email"
/>
<csrf disabled="true"/>
<logout
logout-url="/sign/out"
/>
</http>
<authentication-manager erase-credentials="false">
<authentication-provider user-service-ref="customUserDetailsService">
</authentication-provider>
</authentication-manager>
<beans:bean id="customUserDetailsService" class="milkiv.easyword.controller.sign.CustomUserDetailsService"/>
</beans:beans>
Und mein Test-Controller:
@RequestMapping(method = RequestMethod.POST, path = "/user/changepassword")
public String changePassword(@Valid @ModelAttribute("userPasswordChange") final UserPasswordChange userPasswordChange, BindingResult bindingResult, ModelMap model) {
if (SecurityContextHolder.getContext().getAuthentication().getPrincipal() instanceof UserDetails){
//PRINTED
System.out.println("UserDetails +");
}
if(SecurityContextHolder.getContext().getAuthentication().getPrincipal().getClass()==LoggedUser.class){
//NOT PRINTED
System.out.println("LoggedUser +");
}
try {
LoggedUser ud = (LoggedUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
} catch (Exception ex) {
//PRINTED
System.out.println("LoggedUser -");
}
//class org.springframework.security.core.userdetails.User
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal().getClass());
return "user/myInfo";
}
EDIT: Stack Trace
testChangeUserPassword(milkiv.easyword.controller.sign.UserTest) Time elapsed: 0.896 sec <<< ERROR!
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to milkiv.easyword.controller.sign.LoggedUser
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:158)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:156)
at milkiv.easyword.controller.sign.UserTest.testChangeUserPassword(UserTest.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:85)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:86)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:241)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:292)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:180)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:242)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:137)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
Caused by: java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to milkiv.easyword.controller.sign.LoggedUser
at milkiv.easyword.controller.User.changePassword(User.java:67)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
... 77 more
Results :
Tests in error:
testChangeUserPassword(milkiv.easyword.controller.sign.UserTest): Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to milkiv.easyword.controller.sign.LoggedUser
EDIT: TEST
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:resources/spring-context.xml", "classpath:resources/spring-security.xml"})
@WebAppConfiguration
public class UserTest {
MockMvc mockMvc;
Storages storages;
@Autowired
private Filter springSecurityFilterChain;
@Autowired
WebApplicationContext wac; // cached
@Before
public void doBeforeTests() {
mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.addFilters(springSecurityFilterChain)
.build();
ApplicationContext context = new ClassPathXmlApplicationContext("resources/spring-context.xml", "resources/spring-security.xml");
storages = context.getBean(Storages.class);
}
@Test
public void testChangeUserPassword() throws Exception {
mockMvc.perform(post("/user/changepassword").with(user("[email protected]").password("test"))
)
.andExpect(status().isOk());
}
}
Der Test wird Ihre benutzerdefinierten Details nicht verwenden, es wird einen regulären Spring Security-Benutzer erstellen. Daher der Classcast. Sie sollten eigentlich gegen die 'UserDetails'-Schnittstelle programmieren, nicht die konkrete Implementierung des Benutzers. Um es zu verwenden, erstellen Sie eine Instanz des 'LoggedUser' und übergeben Sie diese an die 'user' Methode. Das wird in diesem Blogpost https://spring.io/blog/2014/05/23/preview-spring-security-test-web-security#user-content-populating-a-test-user-with-annotations erklärt –