关于如何在SpringSecurityBean中模拟自定义UserServiceDetails进行单元测试?和springsecurity自定义认证的问题就给大家分享到这里,感谢你花时间阅读本站内容,
关于如何在SpringSecurity Bean中模拟自定义UserServiceDetails进行单元测试?和springsecurity自定义认证的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于java – Spring Security:未调用自定义UserDetailsService(使用Auth0身份验证)、java – 无法自动连线字段:private org.springframework.security.core.userdetails.UserDetailsService、Null传递给Spring Security UserDetailsService、org.springframework.security.core.userdetails.AuthenticationUserDetailsService的实例源码等相关知识的信息别忘了在本站进行查找喔。
本文目录一览:- 如何在SpringSecurity Bean中模拟自定义UserServiceDetails进行单元测试?(springsecurity自定义认证)
- java – Spring Security:未调用自定义UserDetailsService(使用Auth0身份验证)
- java – 无法自动连线字段:private org.springframework.security.core.userdetails.UserDetailsService
- Null传递给Spring Security UserDetailsService
- org.springframework.security.core.userdetails.AuthenticationUserDetailsService的实例源码
如何在SpringSecurity Bean中模拟自定义UserServiceDetails进行单元测试?(springsecurity自定义认证)
我已Spring Boot启用基本身份验证的应用程序。UserServiceDetails
从数据库消耗。为了进行单元测试,我想对其进行模拟,以便从其他地方使用数据。
我该怎么做?
我的问题不是如何模拟UserServiceDetails
自身,而是如何模拟使用它来通过基本身份验证测试Controller的方式。
以下是我的SpringSecurity配置:
@Configuration@EnableGlobalMethodSecurity(securedEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter { private static final String[] AUTH_WHITELIST = { // -- swagger ui "/", "/csrf", "/swagger-resources", "/swagger-resources/**", "/configuration/ui", "/configuration/security", "/swagger-ui.html", "/webjars/**" }; @Autowired @Qualifier("customUserDetailsService") private UserDetailsService userDetailsService; private final static Integer bCryptEncryptionLevel = 8; @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(bCryptEncryptionLevel); } public SecurityConfig() { super(); } @Autowired public void configureGlobalSecurity(AuthenticationManagerBuilder authManagerBuilder) throws Exception { authManagerBuilder.authenticationProvider(authenticationProvider()); authManagerBuilder.userDetailsService(userDetailsService) .passwordEncoder(bCryptPasswordEncoder()); } @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); authenticationProvider.setUserDetailsService(userDetailsService); authenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); return authenticationProvider; } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(AUTH_WHITELIST).permitAll() // allow default swagger docket .regexMatchers("\\A/v2/api-docs\\Z").permitAll() // require auth for any other swagger docket .regexMatchers("\\A/v2/api-docs?.*\\Z").authenticated() .antMatchers("/**").authenticated() .and() .httpBasic() .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }}
总之,我怎么能嘲笑UserServiceDetails到SpringSecurity配置,所以我能单元测试控制器到我的Spring Boot应用程序?
答案1
小编典典我认为您应该使用@Profile
,这 UserDetailesService
是一个接口,它创建的两个实现UserDetailesService
,一个用于测试,另一个用于其余情况
@Component@Profile("test")public class UserDetailesServiceTestImpl implements UserDetailesService{}@Component@Profile("!test")public class UserDetailesServiceImpl implements UserDetailesService{}
java – Spring Security:未调用自定义UserDetailsService(使用Auth0身份验证)
我正在使用Auth0来保护我的API,这非常有效.我的设置& config与Auth0文档中的suggested setup相同:
// SecurityConfig.java @Configuration @EnableWebSecurity(debug = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { // auth0 config vars here @Override protected void configure(HttpSecurity http) { JwtWebSecurityConfigurer .forRS256(apiAudience,issuer) .configure(http) .authorizeRequests() .antMatchers(HttpMethod.GET,"/api/public").permitAll() .antMatchers(HttpMethod.GET,"/api/private").authenticated(); } }
使用此设置,spring安全主体从jwt标记设置为userId(sub):auth0 | 5b2b ….但是,我希望它设置为匹配用户(来自我的数据库)而不仅仅是userId .我的问题是如何做到这一点.
我试过的
我已经尝试实现从this tutorial复制的自定义数据库支持的UserDetailsService.
然而,无论我如何尝试将其添加到我的conf中,它都不会被调用.我尝试过几种不同的方式添加它没有效果:
// SecurityConfig.java (changes only) // My custom userDetailsService,overriding the loadUserByUsername // method from Spring Framework's UserDetailsService. @Autowired private MyUserDetailsService userDetailsService; protected void configure(HttpSecurity http) { http.userDetailsService(userDetailsService); // Option 1 http.authenticationProvider(authenticationProvider()); // Option 2 JwtWebSecurityConfigurer [...] // The rest unchanged from above } @Override // Option 3 & 4: Override the following method protected void configure(AuthenticationManagerBuilder auth) { auth.authenticationProvider(authenticationProvider()); // Option 3 auth.userDetailsService(userDetailsService); // Option 4 } @Bean // Needed for Options 2 or 4 public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setUserDetailsService(userDetailsService); return authProvider; }
不幸的是,由于我需要将其与Auth0身份验证相结合,所以没有类似的“userDetails not called called”问题帮助了我.
我并不认为我正走在正确的道路上.我觉得很奇怪,在这个极为常见的用例中我找不到Auth0的任何文档,所以也许我错过了一些明显的东西.
PS:不确定是否相关,但在init期间始终记录以下内容.
Jun 27,2018 11:25:22 AM com.test.UserRepository initDao INFO: No authentication manager set. Reauthentication of users when changing passwords will not be performed.
编辑1:
根据Ashish451的回答,我尝试复制他的CustomUserDetailsService,并将以下内容添加到我的SecurityConfig中:
@Autowired private CustomUserDetailsService userService; @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Autowired public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception { auth.userDetailsService( userService ); }
不幸的是,通过这些更改,仍未调用CustomUserDetailsService.
编辑2:
添加@norberto Ritzmann建议的日志记录方法时的输出:
Jul 04,2018 3:49:22 PM com.test.repositories.UserRepositoryImpl initDao INFO: No authentication manager set. Reauthentication of users when changing passwords will not be performed. Jul 04,2018 3:49:22 PM com.test.runner.JettyRunner testUserDetailsImpl INFO: UserDetailsService implementation: com.test.services.CustomUserDetailsService
解决方法
我不知道什么是apiAudience,发行人,但它产生了JWT的子猜测.
您的问题是您希望根据数据库更改JWT子.
我最近在Spring Boot Application中实现了JWT安全性.
我从数据库中获取UserName之后设置它.
为清楚起见,我添加了带pkg信息的代码.
//我的适配器类和你的一样,除了我添加过滤器的一件事.在此过滤器中,我正在验证JWT令牌.每次触发安全休息URL时都会调用此过滤器.
import java.nio.charset.StandardCharsets; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; 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.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.thymeleaf.Spring5.SpringTemplateEngine; import org.thymeleaf.Spring5.templateresolver.SpringResourceTemplateResolver; import com.dev.myapp.jwt.model.CustomUserDetailsService; import com.dev.myapp.security.RestAuthenticationEntryPoint; import com.dev.myapp.security.TokenAuthenticationFilter; import com.dev.myapp.security.TokenHelper; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Autowired private CustomUserDetailsService jwtUserDetailsService; // Get UserDetail bu UserName @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; // Handle any exception during Authentication @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // Binds User service for User and Password Query from Database with Password Encryption @Autowired public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception { auth.userDetailsService( jwtUserDetailsService ) .passwordEncoder( passwordEncoder() ); } @Autowired TokenHelper tokenHelper; // Contains method for JWT key Generation,Validation and many more... @Override protected void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ).and() .exceptionHandling().authenticationEntryPoint( restAuthenticationEntryPoint ).and() .authorizeRequests() .antMatchers("/auth/**").permitAll() .anyRequest().authenticated().and() .addFilterBefore(new TokenAuthenticationFilter(tokenHelper,jwtUserDetailsService),BasicAuthenticationFilter.class); http.csrf().disable(); } // Patterns to ignore from JWT security check @Override public void configure(WebSecurity web) throws Exception { // TokenAuthenticationFilter will ignore below paths web.ignoring().antMatchers( HttpMethod.POST,"/auth/login" ); web.ignoring().antMatchers( HttpMethod.GET,"/","/assets/**","/*.html","/favicon.ico","/**/*.html","/**/*.css","/**/*.js" ); } }
//用户服务获取用户详细信息
@Transactional @Repository public class CustomUserDetailsService implements UserDetailsService { protected final Log LOGGER = LogFactory.getLog(getClass()); @Autowired private UserRepo userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User uu = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException(String.format("No user found with username '%s'.",username)); } else { return user; } } }
//未经授权的访问处理程序
@Component public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException { // This is invoked when user tries to access a secured REST resource without supplying any credentials response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException.getMessage()); } }
//用于验证JWT令牌的过滤链
import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.servletexception; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.web.filter.OncePerRequestFilter; public class TokenAuthenticationFilter extends OncePerRequestFilter { protected final Log logger = LogFactory.getLog(getClass()); private TokenHelper tokenHelper; private UserDetailsService userDetailsService; public TokenAuthenticationFilter(TokenHelper tokenHelper,UserDetailsService userDetailsService) { this.tokenHelper = tokenHelper; this.userDetailsService = userDetailsService; } @Override public void doFilterInternal( HttpServletRequest request,FilterChain chain ) throws IOException,servletexception { String username; String authToken = tokenHelper.getToken(request); logger.info("AuthToken: "+authToken); if (authToken != null) { // get username from token username = tokenHelper.getUsernameFromToken(authToken); logger.info("UserName: "+username); if (username != null) { // get user UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (tokenHelper.validatetoken(authToken,userDetails)) { // create authentication TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails); authentication.setToken(authToken); SecurityContextHolder.getContext().setAuthentication(authentication); // Adding Token in Security COntext } }else{ logger.error("Something is wrong with Token."); } } chain.doFilter(request,response); } }
// TokenBasedAuthentication类
import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; public class TokenBasedAuthentication extends AbstractAuthenticationToken { private static final long serialVersionUID = -8448265604081678951L; private String token; private final UserDetails principle; public TokenBasedAuthentication( UserDetails principle ) { super( principle.getAuthorities() ); this.principle = principle; } public String getToken() { return token; } public void setToken( String token ) { this.token = token; } @Override public boolean isAuthenticated() { return true; } @Override public Object getCredentials() { return token; } @Override public UserDetails getPrincipal() { return principle; } }
// JWT生成和验证逻辑的助手类
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import com.dev.myapp.common.TimeProvider; import com.dev.myapp.entity.User; @Component public class TokenHelper { protected final Log LOGGER = LogFactory.getLog(getClass()); @Value("${app.name}") // reading details from property file added in Class path private String APP_NAME; @Value("${jwt.secret}") public String SECRET; @Value("${jwt.licenseSecret}") public String LICENSE_SECRET; @Value("${jwt.expires_in}") private int EXPIRES_IN; @Value("${jwt.mobile_expires_in}") private int MOBILE_EXPIRES_IN; @Value("${jwt.header}") private String AUTH_HEADER; @Autowired TimeProvider timeProvider; // return current time. Basically Deployment time. private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512; // Generate Token based on UserName. You can Customize this public String generatetoken(String username) { String audience = generateAudience(); return Jwts.builder() .setIssuer( APP_NAME ) .setSubject(username) .setAudience(audience) .setIssuedAt(timeProvider.Now()) .setExpiration(generateExpirationDate()) .signWith( SIGNATURE_ALGORITHM,SECRET ) .compact(); } public Boolean validatetoken(String token,UserDetails userDetails) { User user = (User) userDetails; final String username = getUsernameFromToken(token); final Date created = getIssuedAtDateFromToken(token); return ( username != null && username.equals(userDetails.getUsername()) ); } // If Token is valid will extract all claim else throw appropriate error private Claims getAllClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); } catch (Exception e) { LOGGER.error("Could not get all claims Token from passed token"); claims = null; } return claims; } private Date generateExpirationDate() { long expiresIn = EXPIRES_IN; return new Date(timeProvider.Now().getTime() + expiresIn * 1000); } }
对于这个日志
No authentication manager set. Reauthentication of users when changing passwords
由于尚未使用Name loadUserByUsername实现方法.你得到这个日志.
编辑1:
我正在使用Filter Chain来验证令牌并在安全上下文中添加用户,这将从令牌中提取….
使用JWT并且您使用的是AuthO,只有实现不同.我为完整的工作流程添加了完整的实施.
您专注于从WebSecurityConfig类实现authenticationManagerBean和configureGlobal以使用UserService.
和TokenBasedAuthentication类实现.
其他你可以跳过的东西.
java – 无法自动连线字段:private org.springframework.security.core.userdetails.UserDetailsService
每次运行我的应用程序,我得到:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘securityConfig’: Injection of autowired dependencies Failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.security.core.userdetails.UserDetailsService com.entirety.app.config.SecurityConfig.userDetailsServiceImplementation; nested exception is org.springframework.beans.factory.NoSuchBeanDeFinitionException: No qualifying bean of type [org.springframework.security.core.userdetails.UserDetailsService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我已经通过一个精细的梳子我的代码,无法确定的问题.
Spring Framework版本:3.2.5.RELEASE
Spring Security版本:3.2.0.M2
SecurityConfig.java
package com.entirety.app.config; import com.entirety.app.service.implement.UserDetailsServiceImplementation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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 javax.sql.DataSource; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled=true) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Autowired private UserDetailsService userDetailsServiceImplementation; @Override protected void registerauthentication(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication().dataSource(dataSource); } @Override protected void configure(HttpSecurity http) throws Exception { http.userDetailsService(userDetailsServiceImplementation) .authorizeUrls() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/sec/**").hasRole("MODERATOR") .antMatchers("/*").permitAll() .anyRequest().anonymous().and().exceptionHandling().accessDeniedPage("/denied").and() .formLogin() .loginProcessingUrl("/j_spring_security_check") .loginPage("/login") .failureUrl("/error-login") .and() .logout() .logoutUrl("/j_spring_security_logout") .logoutSuccessUrl("/"); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
CrmUserService.java
@Service("userService") @Transactional public class CrmUserService implements UserDetailsService { @Autowired private UserDAO userDAO; @Override public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException { com.entirety.app.domain.User domainUser = userDAO.getUser(login); boolean enabled = true; boolean accountNonExpired = true; boolean credentialsNonExpired = true; boolean accountNonLocked = true; return new User( domainUser.getLogin(),domainUser.getpassword(),enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,getAuthorities(domainUser.getAuthority().getId()) ); } public Collection<? extends GrantedAuthority> getAuthorities(Integer role) { List<GrantedAuthority> authList = getGrantedAuthorities(getRoles(role)); return authList; } public List<String> getRoles(Integer role) { List<String> roles = new ArrayList<String>(); if (role.intValue() == 1) { roles.add("ROLE_MODERATOR"); roles.add("ROLE_ADMIN"); } else if (role.intValue() == 2) { roles.add("ROLE_MODERATOR"); } return roles; } public static List<GrantedAuthority> getGrantedAuthorities(List<String> roles) { List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); for (String role : roles) { authorities.add(new SimpleGrantedAuthority(role)); } return authorities; } }
没有什么逻辑的理由,为什么它应该是失败,但它是.
注意:
我的IDE可以链接自动连接(这是它知道它的自动连接和所有内容),但编译失败.
编辑2:
我已经从SecurityConfig.java文件中删除了以下代码
@Autowired private UserDataService userDataService;
并将其添加到我知道的POJO和控制器之一,并定期调用并包含其他服务.
在这种情况下,将该代码粘贴到我的baseController.java文件中,该文件指向呈现主页.
该服务正确编译,甚至可以在控制器内调用.
所以这个问题仅仅是配置文件,例如SecurityConfig.java,这是唯一的时候它不起作用.
编辑3:添加额外的文件
SecurityInitializer.java
@Order(2) public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer { }
WebInitializer.java
@Order(1) public class WebAppInitializer extends AbstractAnnotationConfigdispatcherServletinitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class<?>[] { PersistanceConfig.class,SecurityConfig.class }; //To change body of implemented methods use File | Settings | File Templates. } @Override protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] {"/"}; } // @Override // protected Filter[] getServletFilters() { // CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); // characterEncodingFilter.setEncoding("UTF-8"); // return new Filter[] { characterEncodingFilter}; // } }
MVC-调度-servlet.xml中
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.entirety.app"/> <bean> <property name="prefix" value="/WEB-INF/pages/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
web.xml中
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Spring MVC Application</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.dispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
解决方法
看来你有多个问题.
web.xml与AbstractAnnotationConfigdispatcherServletinitializer
您创建的应用程序具有配置名为mvc-dispatcher的dispatcherServlet的web.xml. mvc-dispatcher具有mvc-dispatcher-servlet.xml的配置,它加载了com.springapp.sectest包中的所有bean.这意味着mvc-dispatcher可以找到您的UserDetailsService.
该应用程序还有一个AbstractAnnotationConfigdispatcherServletinitializer,它创建了一个dispatcherServlet,它加载了WebConfig java配置.此dispatcherServlet无法看到由mvc-dispatcher创建的任何Bean.
简而言之,您应该使用web.xml或AbstractAnnotationConfigdispatcherServletinitializer配置dispatcherServlet,而不是同时配置dispatcherServlet.
getRootConfigClasses与getServletConfigClasses
getRootConfigClasses中的任何配置通常称为根配置或父配置,并且无法查看getServletConfigClasses中定义的bean. Spring Security使用由@EnableWebSecurity注释创建的名为springSecurityFilterChain的Filter来保护您在getRootConfigClasses中定义的应用程序.这意味着通常最好将Spring Security的配置放在getRootConfigClasses中.有一些例外,就像您想要在Spring MVC控制器上执行方法安全性.
getServletConfigClasses中的任何配置通常称为子配置,并且可以查看在getRootConfigClasses中定义的bean. getServletConfigClasses配置dispatcherServlet,并且必须包含Spring MVC(即控制器,ViewResovlers等)的bean.在getRootConfigClasses中定义的任何Spring MVC bean可见但不被使用.
此设置虽然有点混乱,但您可以使用具有隔离配置的多个dispatcherServlet实例.实际上,很少有多个dispatcherServlet实例.
鉴于上述信息,您应将UserDetailsService声明及其所有依赖bean移动到getRootConfigClasses.这将确保SecurityConfig.java可以找到UserDetailsService.
拉请求修复
我已经提交了一个公关,以获得您的应用程序,使其从没有错误https://github.com/worldcombined/AnnotateFailed/pull/1开始.几个笔记
>测试不使用父和子上下文,因此它不反映运行应用程序
>我不得不移动资源文件夹,以便被正确地拾起
>我没有这样做,所以你实际上可以认证.没有login.jsp,你提供的UserDetailsService总是返回null而不是我试图证明这里的错误被回答了.
原始答案
基于此评论:
@ComponentScan is undertaken in my mvc-dispatcher-servlet.xml as
<context:component-scan base-package=”com.entirety.app”/>
看来您正在配置dispatcher配置中的服务,而不是在根上下文中.
Spring Security的配置通常在根上下文中配置.例如,可以扩展AbstractSecurityWebApplicationInitializer,如下所示:
import org.springframework.security.web.context.*; public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
您如何导入安全配置?如果Spring Security配置在根上下文中,那么它将不会看到在dispatcher Servlet上下文中定义的bean.
解决方案是在根上下文中移动服务配置,或将Spring Security配置移动到调度程序的ApplicationContext.例如,如果调度程序servlet命名为mvc,则将Spring Security配置移动到调度程序上下文将如下所示.
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { protected String getdispatcherWebApplicationContextSuffix() { return "mvc"; } }
Null传递给Spring Security UserDetailsService
我正在尝试使用Spring Security将登录名添加到我的Web应用程序中。
这是我第一次尝试使用纯代码配置添加spring-security 3.2(之前在xml配置和标准UserDetailsService
实现中使用过几次)。
我还看到了其他一些类似的问题,这些问题都移到了3.2上,但都与csrf有关-我目前已禁用这些功能,只是为了排除这些更改。
我的配置如下所示:
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsServiceImpl userDetailsServiceImpl; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .authorizeRequests() .antMatchers("/resources/**").permitAll() .antMatchers("/dashboard").permitAll() .antMatchers("/sign-up").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/") .loginProcessingUrl("/loginprocess") .failureUrl("/?loginFailure=true") .permitAll() .and() .logout() .logoutUrl("/logout") .permitAll(); } @Override protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDetailsServiceImpl) .passwordEncoder(bCryptPasswordEncoder()); } @Bean public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder(); }
我的登录表单如下所示:
<formaction="loginprocess" method="POST"> <h3>Already Registered?</h3> <input type="text"name=''j_username'' placeholder="User name"> <input type="password"name=''j_password'' placeholder="Password"><br/> <inputname="submit" type="submit" value=''Sign in'' > <ahref="#" >Sign up</a></form>
最后,在实现UserDetailsService的过程中,我尝试登录并进行调试和遍历代码:
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException { username = username.toLowerCase();
我看到在这里传递的用户名字符串始终为null-因为所有这些都由底层的spring机制调用,我正在努力查看提交的登录表单中的值正在发生什么。
我已经看过较早,并遍历了包括UsernamePasswordAuthenticationFilter
该类的Spring代码,并且似乎generateUsername(request)调用只是从请求中获取null。
任何人都可以建议出什么问题或哪里可以找到一个好地方吗?春天之前,用户名/密码在请求对象中的哪里设置?
更新
我还尝试过仅更改表单的操作URL并将其发布到普通的MVC控制器-调试已发布的数据存在且在parameterMap中正确-
看来这是安全链中正在删除我的已发布内容来自请求的数据?
答案1
小编典典该文档建议用户名/密码字段必须具有特定名称:-
http://docs.spring.io/spring-security/site/docs/3.2.0.CI-
SNAPSHOT/reference/htmlsingle/#jc
用户名必须作为名为username的HTTP参数存在
密码必须作为名为password的HTTP参数存在
试试看,看看是否可行?
org.springframework.security.core.userdetails.AuthenticationUserDetailsService的实例源码
@Bean public AuthenticationUserDetailsService userDetailsService(){ GrantedAuthorityFromAssertionAttributesUserDetailsService uds = new GrantedAuthorityFromAssertionAttributesUserDetailsService( new String[]{"role"} ); return uds; }
@Bean public PreAuthenticatedAuthenticationProvider preAuthAuthenticationProvider( // final AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> authenticationUserDetailsService) final AuthenticationUserDetailsService authenticationUserDetailsService) { return new PreAuthenticatedAuthenticationProvider(){{ setPreAuthenticatedUserDetailsService(authenticationUserDetailsService); }}; }
@Bean public PreAuthenticatedAuthenticationProvider preAuthAuthenticationProvider(final AuthenticationUserDetailsService authenticationUserDetailsService){ return new PreAuthenticatedAuthenticationProvider(){{ setPreAuthenticatedUserDetailsService(authenticationUserDetailsService); }}; }
/** * 用户自定义的AuthenticationUserDetailsService */ @Bean public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() { return new CustomUserDetailsService(); }
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> x509v3ExtService() { return token -> new User(token.getName(),"",AuthorityUtils.createAuthorityList(ROLE_MTLS_USER)); }
@Bean public AuthenticationUserDetailsService<CasAssertionAuthenticationToken> customUserDetailsService() { return new CustomUserDetailsService(adminList()); }
@Override public void setPreAuthenticatedUserDetailsService(AuthenticationUserDetailsService aPreAuthenticatedUserDetailsService) { this.preAuthenticatedUserDetailsService = aPreAuthenticatedUserDetailsService; super.setPreAuthenticatedUserDetailsService(aPreAuthenticatedUserDetailsService); }
public GuestSupportingUserDetailsService(AuthenticationUserDetailsService<Authentication> nonGuestAuthenticationUserDetailsService) { this.nonGuestAuthenticationUserDetailsService = nonGuestAuthenticationUserDetailsService; }
/** * Set the AuthenticatedUserDetailsService to be used to load the {@code UserDetails} for the authenticated user. * * @param uds The {@link AuthenticationUserDetailsService} to use. */ public void setAuthenticatedUserDetailsService(AuthenticationUserDetailsService<OAuth2AuthenticationToken> uds) { this.authenticatedUserDetailsService = uds; }
关于如何在SpringSecurity Bean中模拟自定义UserServiceDetails进行单元测试?和springsecurity自定义认证的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于java – Spring Security:未调用自定义UserDetailsService(使用Auth0身份验证)、java – 无法自动连线字段:private org.springframework.security.core.userdetails.UserDetailsService、Null传递给Spring Security UserDetailsService、org.springframework.security.core.userdetails.AuthenticationUserDetailsService的实例源码等相关内容,可以在本站寻找。
本文标签: