【发布时间】:2020-08-28 01:47:54
【问题描述】:
早上好,
我正在使用 Spring Security 来保护 Web 应用程序,问题出在身份验证过程中。流程从登录页面开始(显示正确),当我输入“2”作为用户名和“ciao”作为密码时(我解释了我稍后定义的),我被重定向到登录失败页面,似乎没有用户保存在数据库中(尽管日志中没有显示错误或异常)。
我有一个数据库表(使用MySQL),我想使用它来验证用户,建表脚本是
CREATE TABLE IF NOT EXISTS `SR002`.`utenti` (
`id_utente` INT NOT NULL AUTO_INCREMENT,
`nome` VARCHAR(45) NOT NULL,
`cognome` VARCHAR(45) NOT NULL,
`password` VARCHAR(60) NOT NULL,
`qualifica` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id_utente`))
ENGINE = InnoDB;
地点:
- id_utente:用于从DB中获取认证记录(实际上是一种用户名);
- qualifica:用于为每个用户设置权限(例如,如果“qualifica”=“admin”,则用户具有管理员权限)。
我用 BCryptEncoder 手动添加了记录加密密码:
- id_utente = 2
- nome = 名称
- 认知 = 姓氏
- 密码 = $2y$04$atmkYeXsf8f7R6c7LU5fZ.lTuae5Pem3c303mG3P6UVbWMLxtgrSK(它是散列词“ciao”)
- 资格 = 管理员
我发现 Spring Security 为此提供了 JDBC 身份验证,并按照互联网上的教程提出了以下配置和有用的类。
安全配置类
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
@Autowired
PasswordEncoder passwordEncoder;
@Autowired
@Qualifier("securityUserDetailService")
UserDetailsService userDetailsService;
@Autowired
DataSource dataSource;
/**
* Manages the encoder to save passwords in the DB
* not in plain text
*
* @return PasswordEncoder object
*/
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
/**
* Manages the configuration of the Authentication manager with
* user credentials and roles.
* <p>
* The AuthenticationManager processes any authentication request.
*
* @param auth AuthenticationManagerBuilder object
* @throws Exception exception
*/
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder);
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
/**
* Manages the configuration for specific http request.
*
* @param http HttpSecurity request
* @throws Exception exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
//configure security for pages
.antMatchers(new String[]{"/login", "/accessDenied"}).permitAll()
.antMatchers("/**").access("hasRole('admin')")
.anyRequest().authenticated()
//creates login form
.and().formLogin().loginPage("/login").loginProcessingUrl("/login")
.defaultSuccessUrl("/home").failureUrl("/accessDenied")
.usernameParameter("id_utente").passwordParameter("password")
//catches exceptions http 403 response
.and().exceptionHandling().accessDeniedPage("/accessDenied");
}
/**
* Manages the storage of user credentials inside database
*
* @return The authenticationProvider Object
*/
@Bean
public DaoAuthenticationProvider authenticationProvider()
{
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
@Bean
public AuthenticationTrustResolver getAuthenticationTrustResolver()
{
return new AuthenticationTrustResolverImpl();
}
}
自定义用户详情服务
@Service("securityUserDetailService")
public class SecurityUserDetailService implements UserDetailsService
{
@Autowired
UtentiService utentiService;
/**
* Manages the load of a user along with
* ID and password.
*
* @param s user id
* @return UserDetail object with ID and password
* @throws UsernameNotFoundException If user is not found in the system
*/
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException
{
int id = Integer.parseInt(s);
Utenti user = utentiService.findUserById(id);
if(user == null)
{
throw new UsernameNotFoundException("User with id " + s + " not found");
}
return new User(Integer.toString(user.getId_utente()), user.getPassword(), true, true, true, true, getGrantedAuthorities(user));
}
/**
* Determines which roles a particular user has
*
* @param user The user for which to find roles
* @return List containing all roles related to the user
*/
private List<GrantedAuthority> getGrantedAuthorities(Utenti user)
{
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_" + user.getQualifica()));
return authorities;
}
}
我不知道问题出在哪里,但我认为它可能在全局配置中,由于我是 Spring 安全新手,因此我可能会遗漏一些关于此方法的内容:
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder);
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
如何更改这些配置以使用 JDBC 和 MySQL 表对用户进行身份验证? 还有插入第一个(通常是管理员)用户的首选方法吗?
编辑
来自 spring 安全状态的日志:
15:27:38.660 [http-nio-8080-exec-8] ERROR org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: For input string: ""
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:123) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:144) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:219) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:95) ~[spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:141) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:92) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:77) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) [spring-security-web-5.3.1.RELEASE.jar:5.3.1.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) [spring-web-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [catalina.jar:9.0.33]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [catalina.jar:9.0.33]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) [catalina.jar:9.0.33]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [catalina.jar:9.0.33]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) [catalina.jar:9.0.33]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) [catalina.jar:9.0.33]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [catalina.jar:9.0.33]
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:688) [catalina.jar:9.0.33]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [catalina.jar:9.0.33]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [catalina.jar:9.0.33]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373) [tomcat-coyote.jar:9.0.33]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-coyote.jar:9.0.33]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-coyote.jar:9.0.33]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594) [tomcat-coyote.jar:9.0.33]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-coyote.jar:9.0.33]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) [?:?]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-util.jar:9.0.33]
at java.lang.Thread.run(Thread.java:844) [?:?]
Caused by: java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[?:?]
at java.lang.Integer.parseInt(Integer.java:662) ~[?:?]
at java.lang.Integer.parseInt(Integer.java:770) ~[?:?]
at com.entsorgafin.service.impl.security.SecurityUserDetailService.loadUserByUsername(SecurityUserDetailService.java:36) ~[classes/:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.5.RELEASE.jar:5.2.5.RELEASE]
at com.sun.proxy.$Proxy71.loadUserByUsername(Unknown Source) ~[?:?]
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:108) ~[spring-security-core-5.3.1.RELEASE.jar:5.3.1.RELEASE]
... 43 more
特别是行:
Caused by: java.lang.NumberFormatException: For input string: ""
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[?:?]
at java.lang.Integer.parseInt(Integer.java:662) ~[?:?]
at java.lang.Integer.parseInt(Integer.java:770) ~[?:?]
at com.entsorgafin.service.impl.security.SecurityUserDetailService.loadUserByUsername(SecurityUserDetailService.java:36) ~[classes/:?]
说明错误来自该行
int id = Integer.parseInt(s);
在loadUserByUsername 方法中,现在我不明白这个方法是如何得到一个空字符串“”而不是我在表单中输入的“2”。
表单页面
<c:url var="loginUrl" value="/login" />
<div class="leftBar">
<h2>Login</h2>
<p>testo esempio</p>
</div>
<div class="rightBar">
<div class="col-md-7 col-sm-12">
<form:form method="post" action="${loginUrl}">
<div class="form-group">
<label for="userId">ID utente</label>
<input type="text" class="form-control" id="userId" placeholder="ID utente">
</div>
<div class="form-group">
<label for="userPwd">Password</label>
<input type="password" class="form-control" id="userPwd" placeholder="Password">
</div>
<button type="submit" name="submit" value="submit" class="btn">Accedi</button>
</form:form>
</div>
</div>
【问题讨论】:
-
这里已经有很多信息了,但是如果你添加一些 Spring Security 日志,诊断问题会(甚至)更容易。将“logging.level.org.springframework.security=debug”放入你的application.properties,再次运行应用程序,尝试登录并添加你看到的日志。
-
@neofelis,在此先感谢,我添加了来自 Spring Security 的日志。我使用 log4j2 来获取它们,我还根据新发现相应地编辑了帖子。
标签: java spring authentication spring-security