我需要保存一些有关用户的其他信息,以排除过多的数据库请求。所以据我所知,我必须:
1)覆盖UserDetails接口。在此实现中,保存我需要的所有附加信息
2)覆盖UserDetailsService接口。在loadUserByUsername(String email)中,我必须返回UserDetails类的实现。之后,我将能够通过getPrincipal()
方法获取此Object。
如果我在1)或2)中犯了错误,请纠正我。
那么我遇到了什么问题? 实际上,当我尝试getPrincipal()
时,它返回了org.springframework.security.core.userdetails.User
UserDetails接口的实现,我无法投射到LoggedUser,因为从逻辑上讲,ClassCastException
发生了。
提前感谢您的帮助。 附:我也尝试扩展User类,但结果相同。
AuthUserDetailsService (UserDetailsService实施):
@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());
}
}
答案 0 :(得分:1)
而不是:
user("test@mail.com").password("test")
使用:
user(userDetails)
其中userDetails
是您的LoggedUser
类的实例。