未调用 Spring Security Authentication UserDetailsService 实现类
Posted
技术标签:
【中文标题】未调用 Spring Security Authentication UserDetailsService 实现类【英文标题】:Spring Security Authentication UserDetailsService Implementation class not being invoked 【发布时间】:2020-01-03 09:31:14 【问题描述】:我正在尝试从基于 Spring XML 的配置迁移到纯 Java 配置,我已经配置了所有配置文件,但是当我尝试在登录页面中输入用户名和密码时,它再次将我重定向回登录页面.我相信 userServiceImpl 方法没有被调用,因为控件没有去那里。 在这里,我有自动装配的 userServiceImpl 方法,它实现了 Spring Security 核心凭据的 UserDetailsService。
package com.lw.sms.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.orm.hibernate4.LocalSessionFactoryBuilder;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.lw.sms.CustomAuthenticationSuccessHandler;
import com.lw.sms.UserServiceImpl;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@EnableTransactionManagement
@Order(1000)
public class SecurityConfig extends WebSecurityConfigurerAdapter
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class);
@Value("$jdbc.dialect")
String jdbcDialect;
@Value("$jdbc.driverClassName")
String jdbcDriverClassName;
@Value("$jdbc.databaseurl")
String jdbcDatabaseurl;
@Value("$jdbc.username")
String jdbcusername;
@Value("$jdbc.password")
String jdbcPassword;
@Autowired
private CustomAuthenticationSuccessHandler customAuthenticationSuccessHandler;
@Autowired
private UserServiceImpl userServiceImpl;
@Override
public void configure(final AuthenticationManagerBuilder auth) throws Exception
logger.info("configure auth");
auth.userDetailsService(userServiceImpl);
@Bean
public PasswordEncoder passwordEncoder()
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
@Override
public void configure(HttpSecurity http) throws Exception
logger.info("configure http");
http.httpBasic().disable();
http.authorizeRequests().and().formLogin().loginPage("/login").usernameParameter("employeeId")
.passwordParameter("password").successHandler(customAuthenticationSuccessHandler)
.failureUrl("/login?error").defaultSuccessUrl("/dashboard", true)
.loginProcessingUrl("j_spring_security_check")
.and().logout().logoutUrl("/j_spring_security_logout").logoutSuccessUrl("/logout")
.invalidateHttpSession(true).deleteCookies("JSESSIONID")
.and().sessionManagement().invalidSessionUrl("/logout").maximumSessions(1)
.maxSessionsPreventsLogin(true).expiredUrl("/logout");
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
@Bean
public SessionRegistry sessionRegistry()
logger.info("sessionRegistry");
return new SessionRegistryImpl();
@Bean
public SessionFactory sessionFactory()
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource());
builder.scanPackages("com.lw.sms").addProperties(hibernateProperties());
return builder.buildSessionFactory();
@Bean
public LocalSessionFactoryBean hibernateSessionFactory()
logger.info("sessionFactory");
org.hibernate.cfg.Configuration configuration = new org.hibernate.cfg.Configuration();
configuration.configure("hibernate.cfg.xml");
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan("com.lw.sms");
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
@Bean
public DataSource dataSource()
logger.info("dataSource");
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(jdbcDriverClassName);
dataSource.setUrl(jdbcDatabaseurl);
dataSource.setUsername(jdbcusername);
dataSource.setPassword(jdbcPassword);
return dataSource;
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory)
logger.info("transactionManager");
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory);
return htm;
@Bean
public Properties hibernateProperties()
logger.info("hibernateProperties");
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.dialect", jdbcDialect);
hibernateProperties.setProperty("hibernate.default_schema", "sms");
return hibernateProperties;
CustomAuthenticationSuccessHandler 代码附在下面
package com.lw.sms;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component;
import com.lw.sms.constants.Constants;
import com.lw.sms.user.entities.EmployeeEntity;
import com.lw.sms.user.entities.EmployeePermissionsEntity;
import com.lw.sms.user.service.UserManagementService;
import com.lw.sms.util.QueryBuilder;
@Component("customAuthenticationSuccessHandler")
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler
private static final Logger logger = LoggerFactory.getLogger(CustomAuthenticationSuccessHandler.class);
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Autowired
@Qualifier("sessionInfo")
private SessionInfo sessionInfo;
@Autowired
@Qualifier("userManagementServiceImpl")
private UserManagementService userManagementService;
@Autowired
private HttpServletRequest httpServletRequest;
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException
response.setStatus(HttpServletResponse.SC_OK);
clearAuthenticationAttributes(request);
handle(request, response);
protected void handle(HttpServletRequest request, HttpServletResponse response)
throws IOException
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails)
EmployeeEntity employeeEntity = (EmployeeEntity) principal;
setSessionInformationForEmployee(employeeEntity);
if (response.isCommitted())
return;
startServices();
redirectStrategy.sendRedirect(request, response, "/dashboard");
/**
* @param employeeEntity
*
*/
private void setSessionInformationForEmployee(EmployeeEntity employeeEntity)
try
WebAuthenticationDetails details = (WebAuthenticationDetails) SecurityContextHolder.getContext()
.getAuthentication().getDetails();
userManagementService.updateLoginInfo(employeeEntity.getUsername(), details.getRemoteAddress());
// setting session information
catch (Exception e)
logger.info("Exception while set Session Information For Employee :" + ExceptionUtils.getFullStackTrace(e));
private void startServices()
logger.info("Starting Services..");
try
String domainName = httpServletRequest.getScheme() + "://" + httpServletRequest.getServerName();
if (httpServletRequest.getServerPort() != 0)
domainName += ":" + httpServletRequest.getServerPort();
domainName += httpServletRequest.getContextPath();
Constants.domainName = domainName + "/resources";
catch (Exception e)
logger.error("Error in start services :"+ ExceptionUtils.getFullStackTrace(e));
protected void clearAuthenticationAttributes(HttpServletRequest request)
HttpSession session = request.getSession(false);
if (session == null)
return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
public void setRedirectStrategy(RedirectStrategy redirectStrategy)
this.redirectStrategy = redirectStrategy;
protected RedirectStrategy getRedirectStrategy()
return redirectStrategy;
UserServiceImpl 代码附在下面
package com.lw.sms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.lw.sms.user.dao.UserManagementDAO;
import com.lw.sms.user.entities.EmployeeEntity;
@Service("userServiceImpl")
public class UserServiceImpl implements UserDetailsService
@Autowired
@Qualifier("userManagementDAOImpl")
private UserManagementDAO userManagementDAO;
@Override
@Transactional
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException
EmployeeEntity employeeEntity = userManagementDAO.loadUserByUsername(username);
if (employeeEntity == null)
ContractorEntity contractorEntity = userManagementDAO.loadUserByUsernameContractor(username);
if(contractorEntity == null)
throw new AuthenticationCredentialsNotFoundException("Invalid Username or Password");
if(!employeeEntity.isEnabled())
throw new AuthenticationCredentialsNotFoundException("Employee Disabled");
return employeeEntity;
全局异常处理代码:
package com.lw.sms;
import java.io.IOException;
import java.nio.charset.Charset;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.json.simple.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@SuppressWarnings("deprecation")
@Controller
@ControllerAdvice
public class CustomExceptionHandler extends RuntimeException
/**
*
*/
private static final long serialVersionUID = 3699167829164697594L;
private static final Logger logger = LoggerFactory
.getLogger(CustomExceptionHandler.class);
@ExceptionHandler(AuthenticationCredentialsNotFoundException.class)
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView handleLoginExceptions(HttpServletRequest request,
HttpServletResponse response,Exception exception)
logger.info("Handling exception time error"+ exception.getMessage());
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("login");
return modelAndView;
@ExceptionHandler(HttpSessionRequiredException.class)
public String handleSessionExpired()
logger.info("session-expired");
return "logout";
@SuppressWarnings("unchecked")
@ExceptionHandler(value = MissingServletRequestParameterException.class,
ServletRequestBindingException.class, TypeMismatchException.class,
HttpMessageNotReadableException.class,
MethodArgumentNotValidException.class,
MissingServletRequestPartException.class )
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Bad Request")
@ResponseBody
public JSONObject handle400Exception(Exception ex,HttpServletResponse response)
logger.error("400 Exception" + ex.getMessage());
JSONObject jsonObject=new JSONObject();
jsonObject.put("status", HttpStatus.BAD_REQUEST);
jsonObject.put("success", false);
jsonObject.put("message", ex.getMessage());
try
response.getOutputStream().write(jsonObject.toString().getBytes(Charset.defaultCharset()));
catch (IOException e)
logger.error(ExceptionUtils.getFullStackTrace(e));
response.setContentType("application/json; charset=UTF-8");
return jsonObject;
@ExceptionHandler(value = NoSuchRequestHandlingMethodException.class, )
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Not Found")
@ResponseBody
public Boolean handle404Exception(Exception ex)
logger.error("404 Exception" + ExceptionUtils.getFullStackTrace(ex));
return false;
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class )
@ResponseStatus(value = HttpStatus.NOT_ACCEPTABLE, reason = "Not Supported")
@ResponseBody
public Boolean handle405Exception(Exception ex)
logger.error("405 Exception" + ExceptionUtils.getFullStackTrace(ex));
return false;
@ExceptionHandler(value = HttpMessageNotWritableException.class,
ConversionNotSupportedException.class )
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR, reason = "Internal Server Error")
@ResponseBody
public Boolean handle500Exception(Exception ex)
logger.error("500 Exception" + ExceptionUtils.getFullStackTrace(ex));
return false;
@ExceptionHandler( Exception.class )
public ModelAndView handleException(Exception ex, HttpServletRequest request,
HttpServletResponse response)
logger.info("*****************************************************");
logger.error("Exception: " + ExceptionUtils.getFullStackTrace(ex));
ex.printStackTrace();
logger.info("*****************************************************");
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("logout");
return modelAndView;
这是我的 HomeController。
package com.lw.sms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import com.lw.sms.master.service.MasterDataService;
import com.lw.sms.user.entities.EmployeeEntity;
/**
* Handles requests for the application home page.
*/
@Controller("homeController")
public class HomeController
@Autowired
@Qualifier("masterDataServiceImpl")
private MasterDataService masterDataService;
@Autowired
@Qualifier("sessionInfo")
private SessionInfo sessionInfo;
@GetMapping(value = "/home", "/", "login" )
public ModelAndView home()
return new ModelAndView("login");
@GetMapping(value = "dashboard")
public ModelAndView dashboard()
EmployeeEntity user = sessionInfo.getEmployeeEntity();
return new ModelAndView("dashboard", "userDetails", user);
// Logout page
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
@GetMapping(value = "logout")
public String logout()
return "logout";
【问题讨论】:
【参考方案1】:我能够复制错误。 我必须在 SecurityConfig 文件中配置一个新的 bean。
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@Bean
public DaoAuthenticationProvider authenticationProvider()
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userServiceImpl);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
【讨论】:
【参考方案2】:您能否尝试添加successForwardUrl,如下所示。
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/dashboard").access("IS_AUTHENTICATED_FULLY")
.antMatchers("/j_spring_security_check").access("IS_AUTHENTICATED_ANONYMOUSLY")
.and().formLogin().loginPage("/login").usernameParameter("employeeId").passwordParameter("password")
.successForwardUrl("/dashboard").defaultSuccessUrl("/dashboard", true).failureForwardUrl("/loginfailed")
.loginProcessingUrl("/j_spring_security_check")
.and().logout().logoutSuccessUrl("/logout").invalidateHttpSession(true)
.and().sessionManagement().sessionFixation().none()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/login")
.and().exceptionHandling().accessDeniedPage("/Access_Denied").and().csrf().disable();
【讨论】:
嘿 Sujay,现在控件将转到 UserServiceImpl 方法并成功返回,但返回后,控件不会转到 customAuthenticationSuccessHandler,它被困在带有 406 的 @ControllerAdvice 类中。任何想法为什么? 项目中是否写了ControllerAdvice类? 这就像整个项目的全局异常处理程序,如果我们错过了在任何地方处理异常,它就会在这里处理。我在这里更新了代码。控件落在 405 Exception 方法中。 由于您获得的方法不受支持,请检查具有请求映射 /dashboard 的控制器方法,这是成功的 url。它应该是一个获取请求。 嗨 Sujay,我已经使用AbstractAnnotationConfigDispatcherServletInitializer
更改了 AppInitializer 代码,并使用 AbstractSecurityWebApplicationInitializer
更改了 Spring 安全性,并参考了 this 文章。现在它工作正常。以上是关于未调用 Spring Security Authentication UserDetailsService 实现类的主要内容,如果未能解决你的问题,请参考以下文章
Spring security - 未调用自定义 userDetailsService 实现
Spring Security:DB 和 applicationContext 中的密码编码
未调用 Spring Security Authentication UserDetailsService 实现类