在spring mvc应用程序中,我在登录屏幕上捕获一个额外的“location”参数,并使用它进行身份验证以及用户名。所以在'loadUserByUsername'中我的sql查询类似于
select from user where username = ? and location = ?
现在,如果用户是remember-me用户,则无法捕获'location'参数,因为没有登录提示。在记住我的功能的情况下,spring只存储cookie中的用户名。为了记住我,登录然后从cookie检索该用户名并将其传递给'loadUserByUsername'调用以从DB加载用户。因此,在我的情况下,对于remember-me用户,由于'location'为空,因此加载用户的查询失败
我想知道是否有办法覆盖默认弹簧行为并在cookie中存储'location'以及用户名,然后在PersistentTokenBasedRememberMeServices.processAutoLoginCookie()中将位置和用户名传递给'loadUserByUsername'。
请参阅下面的代码以供参考,
CustomAuthenticationFilter.java: -
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
final Long locationId = Long.parseLong(request.getParameter("locations"));
request.getSession().setAttribute("LOCATION_ID", locationId);
return super.attemptAuthentication(request, response);
}
}
SecurityConfig.java: -
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
@Autowired
private AuthenticationManagerBuilder auth;
@Autowired
public void configureGlobal(UserDetailsService userDetailsService, AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
AccessDeniedExceptionHandler accessDeniedExceptionHandler;
@Bean
public CustomInvalidSessionStrategy invalidSessionStrategy() {
return new CustomInvalidSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/error/**").permitAll()
.antMatchers("/secured/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
// .defaultSuccessUrl("/")
.permitAll()
.and().rememberMe().rememberMeServices(persistentTokenBasedRememberMeServices())
.and()
.logout()
.permitAll()
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedExceptionHandler);
http.addFilterBefore(customAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
http.addFilterAfter(rememberMeAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() {
AuthenticationManager manager = null;
try {
manager = super.authenticationManagerBean();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return manager;
}
@Bean
public SimpleUrlAuthenticationSuccessHandler simpleUrlAuthenticationSuccessHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/");
return handler;
}
@Bean
public SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler();
handler.setDefaultFailureUrl("/login?error");
return handler;
}
@Bean
public CustomAuthenticationFilter customAuthenticationFilter () {
CustomAuthenticationFilter filter= new CustomAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login","POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("username");
filter.setPasswordParameter("password");
filter.setAuthenticationSuccessHandler(simpleUrlAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
return filter;
}
@Bean
public RememberMeAuthenticationFilter rememberMeAuthenticationFilter() {
RememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
return filter;
}
@Bean
public PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices service = new PersistentTokenBasedRememberMeServices("remember_me_key", userDetailsService, persistentTokenRepository());
service.setCookieName("remember_me");
service.setTokenValiditySeconds(864000);
return service;
}
@Autowired
public UserDetailsService userDetailsService;
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
}
答案 0 :(得分:0)
如果要覆盖默认弹簧行为并在cookie中存储额外参数,则应实现Spring的接口UserDetails
。有一个例子如何做到
package example.userdetails;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class CustomUserDetails implements UserDetails {
private long id;
private String firstName;
private String lastName;
private String login;
private String password;
private boolean isAccountNonExpired;
private boolean isAccountNonLocked;
private boolean isCredentialsNonExpired;
private boolean isEnabled;
private Collection<? extends GrantedAuthority> authorities;
public CustomUserDetails(long id, String firstName, String lastName, String login, String password, boolean isEnabled, Collection<? extends GrantedAuthority> authorities) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.login = login;
this.password = password;
this.authorities = authorities;
this.isEnabled = isEnabled;
this.isCredentialsNonExpired = true;
this.isAccountNonLocked = true;
this.isAccountNonExpired = true;
}
public long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return login;
}
@Override
public boolean isAccountNonExpired() {
return isAccountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return isAccountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return isCredentialsNonExpired;
}
@Override
public boolean isEnabled() {
return isEnabled;
}
public boolean hasRole(String role) {
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals(role)) {
return true;
}
}
return false;
}
}
要获取登录用户,您可以使用
CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
要使用loadUserByUsername
,您应该实现接口UserDetailsService
。例如
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserDAO userDAO;
@Override
@Transactional
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
User user = userDAO.getUserByLogin(userName);
if (user == null) {
throw new UsernameNotFoundException("Wrong login");
}
List<GrantedAuthority> authorities = buildUserAuthority(user.getRoles());
return new CustomUserDetails(user.getUserId(), user.getFirstName(), user.getLastName(), user.getLogin(), user.getPassword(), authorities);
}
private List<GrantedAuthority> buildUserAuthority(Set<Role> roles) {
Set<GrantedAuthority> authoritySet = roles.stream().map(role -> new SimpleGrantedAuthority(buildRoleForAuthorization(role.getRole()))).collect(Collectors.toSet());
return new ArrayList<>(authoritySet);
}
}
答案 1 :(得分:0)
扩展类PersistentTokenBasedRememberMeServices并覆盖其方法,如下所示
@Override
processAutoLoginCookie(String[] cookieTokens,HttpServletRequest request, HttpServletResponse response){
super.processAutoLoginCookie(cookieTokens,request,response); // do not provide any implementation to loadUserByUsername() in your CustomUserDetail and add one more method loadUserbyUsenameAndLocation(username, location);
String location = request.getSession().getAttribute("LOCATION_ID");
return ((customeUserDetailsService)userDetailsService).loadUserbyUsenameAndLocation(username, location)
}