如何修复UserDetails实现的ClassCastException?

时间:2016-04-20 04:42:10

标签: java spring spring-mvc authentication spring-security

我需要保存一些有关用户的其他信息,以排除过多的数据库请求。所以据我所知,我必须:
1)覆盖UserDetails接口。在此实现中,保存我需要的所有附加信息 2)覆盖UserDetailsS​​ervice接口。在loadUserByUsername(String email)中,我必须返回UserDetails类的实现。之后,我将能够通过getPrincipal()方法获取此Object。

如果我在1)或2)中犯了错误,请纠正我。 那么我遇到了什么问题? 实际上,当我尝试getPrincipal()时,它返回了org.springframework.security.core.userdetails.User UserDetails接口的实现,我无法投射到LoggedUser,因为从逻辑上讲,ClassCastException发生了。

提前感谢您的帮助。 附:我也尝试扩展User类,但结果相同。

AuthUserDetailsS​​ervice (UserDetailsS​​ervice实施):

@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实施):

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();
    }
}

弹簧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>

我的测试控制器

    @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";
        }

编辑:堆栈跟踪

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

编辑:测试

@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("test@mail.com").password("test"))
    )
        .andExpect(status().isOk());
    }
}

1 个答案:

答案 0 :(得分:1)

而不是:

user("test@mail.com").password("test")

使用:

user(userDetails)

其中userDetails是您的LoggedUser类的实例。