I have searched the web for solutions but have not found any. This is my last resort.
2018-04-29 16:31:57.743 WARN 71317 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'authorizationServerConfig': Unsatisfied dependency expressed through field 'authenticationManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'resourceServerConfig': Unsatisfied dependency expressed through field 'authenticationManager'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'org.springframework.security.authenticationManager': Requested bean is currently in creation: Is there an unresolvable circular reference?
CLASS:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends
AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("ClientId")
.secret("secret")
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
CLASS:
import org.springframework.beans.factory.annotation.Autowired;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
#EnableResourceServer
#Configuration
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService customUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.userDetailsService(customUserDetailsService);
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Related
I got error in encoding my inMemory User password using BCryptPasswordEncoder
here is my springsecurityconfig file
SpringSecurityConfig class
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("password"))
.roles("USER");
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
How can i properly encode the password without getting bean creation error
You can use a more component-based approach to do that since the WebSecurityConfigurerAdapter has been deprecated in 5.7.0-M2.
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig {
#Bean
public SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
return http.build()
}
#Bean
public UserDetailsService userDetailsService(PasswordEncoder encoder) {
User admin = User.withUsername("admin")
.password(encoder.encode("password"))
.roles("USER").build();
return new InMemoryUserDetailsManager(admin);
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
This way, you have in-memory authentication by providing a InMemoryUserDetailsManager as the implementation of UserDetailsService, and this bean is dependent on the PasswordEncoder.
You can find more detail on the reference docs.
I'm using in-memory auth to have my login working in spring
However, I want to change it now to persistent database
Please see code below:
JWTWebSecurityConfig
package com.sbc.cpex.security.jwt;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
#Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
#Value("${jwt.get.token.uri}")
private String authenticationPath;
#Autowired
DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// auth
// .userDetailsService(jwtInMemoryUserDetailsService)
// .passwordEncoder(passwordEncoderBean());
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password"
+ " from users where username=?")
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
// .csrf().and().cors().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
#Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/" //Other Stuff You want to Ignore
)
.and()
.ignoring()
.antMatchers("/h2-console/**/**");//Should not be in Production!
}
}
Steps I did:
1. Comment out these lines
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
Add the following lines for jdbcauth
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password"
+ " from users where username=?")
.passwordEncoder(passwordEncoderBean());
Create a class called DataSourceConfig
package com.sbc.cpex.security.jwt;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.username("test");
dataSourceBuilder.password("pass");
return dataSourceBuilder.build();
}
}
But I'm getting this error
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'h2Console' defined in class path resource [org/springframework/boot/autoconfigure/h2/H2ConsoleAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.web.servlet.ServletRegistrationBean]: Factory method 'h2Console' threw exception; nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
Please enlighten me. TIA
The stack trace itself is self explanatory :
nested exception is java.lang.IllegalArgumentException: dataSource or dataSourceClassName or jdbcUrl is required.
You need to provide the Datasource url when creating a bean of Datasource.
From the stacktrace I could understand that you are using H2 . So, you could create a bean like :
#Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.driverClassName("org.h2.Driver");
dataSourceBuilder.url("jdbc:h2:mem:test");
dataSourceBuilder.username("username");
dataSourceBuilder.password("");
return dataSourceBuilder.build();
}
In your code, you only provided the username and password part, hence it is throwing the error.
#iamjpcbau Springboot is autoconfiguring H2 as the databse as it is found as a dependency during class path scanning. Since you have provided a Datasource bean , spring automatically takes it up for configuring H2 but the url is missing , which causes the exception that you are receiving.
Inorder to configure another database with your project, configure the database through application.properties or application.yml or manually create configuration beans so that the configuration for your corresponding database is taken up at startup instead of H2.Now, since there are no other database configured and since H2 is found on classpath , spring is configuring that by default.
I'm using sprig security 4.0.4.RELEASE in my Spring 4.2.5.RELEASE application.
I want to implement the role wise security on method level in my application using #Secured annotation
I'm tried to implement this by adding #EnableGlobalMethodSecurity(securedEnabled = true) but some error is occurring.
Here is my SecurityConfiguration Class
package com.application.security;
import com.application.security.LoginSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
LoginSuccessHandler loginSuccessHandler;
#Autowired
PersistentTokenRepository tokenRepository;
/*#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}*/
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(customDaoAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().invalidSessionUrl("/logout");
http.authorizeRequests()
/*.antMatchers("/").access("hasRole('USER') or hasRole('ADMIN') or hasRole('DBA')")*/
.antMatchers("/registration").permitAll()
.antMatchers("/exclusion").permitAll()
.antMatchers("/landing").permitAll()
.antMatchers("/uploadSingle").permitAll()
.antMatchers("/uploadSingleNoFile").permitAll()
.antMatchers("/loadHtmlTableAjax").permitAll()
.antMatchers("/AllclaimDetails").permitAll()
.antMatchers("/deleteclaim").permitAll()
.antMatchers("/claimComplete").permitAll()
.antMatchers("/exclusionComplete").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.loginProcessingUrl("/login").usernameParameter("username").passwordParameter("password")
.successHandler(loginSuccessHandler)
.failureUrl("/login?error=true")
.and()
.logout()
.logoutUrl("/logout")
.deleteCookies("JSESSIONID")
.permitAll()
.and()
.rememberMe().rememberMeParameter("remember-me").tokenRepository(tokenRepository).tokenValiditySeconds(86400)
.and()
.csrf()
.and()
.exceptionHandling().accessDeniedPage("/Access_Denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", "/images/**","/logoff");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
#Bean
CustomDaoAuthenticationProvider customDaoAuthenticationProvider() {
CustomDaoAuthenticationProvider customAuthenticationProvider = new CustomDaoAuthenticationProvider();
customAuthenticationProvider.setUserDetailsService(userDetailsService);
customAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return customAuthenticationProvider;
}
#Bean
public PersistentTokenBasedRememberMeServices getPersistentTokenBasedRememberMeServices() {
PersistentTokenBasedRememberMeServices tokenBasedservice = new PersistentTokenBasedRememberMeServices(
"remember-me", userDetailsService, tokenRepository);
return tokenBasedservice;
}
#Bean
public AuthenticationTrustResolver getAuthenticationTrustResolver() {
return new AuthenticationTrustResolverImpl();
}
}
When I'm giving #EnableGlobalMethodSecurity(securedEnabled = true) , the application is not getting started. I'm getting the below error.
The problem is from AuthenticationTrustResolver bean
AnnotationConfigWebApplicationContext:546 - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfig': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'metaDataSourceAdvisor': Cannot resolve reference to bean 'methodSecurityMetadataSource' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.setAuthenticationTrustResolver(org.springframework.security.authentication.AuthenticationTrustResolver); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'securityConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter.setTrustResolver(org.springframework.security.authentication.AuthenticationTrustResolver); nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'getAuthenticationTrustResolver': Requested bean is currently in creation: Is there an unresolvable circular reference?
Can somebody tell me the reason?
Please create Initializer class by extending AbstractSecurityWebApplicationInitializer.
Please refer my answer in here.
No bean named 'springSecurityFilterChain' available
I solved this problem by updating my spring security dependency to latest in pom.xml
I changed the version from
<springsecurity.version>4.0.4.RELEASE</springsecurity.version>
to
<springsecurity.version>4.2.3.RELEASE</springsecurity.version>
I have run into a strange problem. I created a project with Spring Boot 2.0.1 with redis, mongodb and elasticsearch . Before adding elasticsearch, everything runs smoothly, but after I add elasticsearch, Spring Boot starts complaining,but the error looks like not related to elasticsearch, it complains that it could not create userRepo 。 Please get noticed I used lombok's #RequiredArgsConstructor to generate constructor to make injection work, so it should not be #autowired issue, Anyone can help me out? thank in adavance
2018-05-02 16:12:58.687 INFO 74244 --- [ restartedMain] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2018-05-02 16:12:59.037 WARN 74244 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/config/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/security/UserDetailsServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepo': Invocation of init method failed; nested exception is java.lang.IllegalStateException: No association found!
2018-05-02 16:12:59.037 DEBUG 74244 --- [ restartedMain] h.i.c.PoolingHttpClientConnectionManager : Connection manager is shutting down
2018-05-02 16:12:59.037 DEBUG 74244 --- [ restartedMain] h.i.c.PoolingHttpClientConnectionManager : Connection manager shut down
Process finished with exit code 1
my subproject's build.gradle is as follows:
apply plugin: 'org.springframework.boot'
configurations {
compile.exclude module: 'spring-boot-starter-tomcat'
}
bootRun {
systemProperties = System.properties as Map<String, ?>
}
test {
systemProperties['spring.profiles.active'] = 'test'
}
dependencies {
implementation("io.springfox:springfox-swagger2:${springFoxVersion}")
implementation("io.springfox:springfox-bean-validators:${springFoxVersion}")
implementation("io.springfox:springfox-swagger-ui:${springFoxVersion}")
implementation("org.springframework.boot:spring-boot-starter-undertow")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("io.jsonwebtoken:jjwt:0.9.0")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-aop")
implementation("org.zalando:problem-spring-web:0.20.1")
implementation("org.redisson:redisson:${redissonVersion}")
implementation("com.fasterxml.jackson.module:jackson-module-afterburner")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb")
implementation("org.springframework.boot:spring-boot-starter-data-redis")
implementation("com.github.vanroy:spring-boot-starter-data-jest:3.1.2.RELEASE")
testImplementation("org.springframework.security:spring-security-test")
}
The UserRepo is as follows
package dev.local.gtm.api.repository;
import dev.local.gtm.api.domain.User;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
#Repository
public interface UserRepo extends MongoRepository<User, String> {
String USERS_BY_LOGIN_CACHE = "usersByLogin";
String USERS_BY_MOBILE_CACHE = "usersByMobile";
String USERS_BY_EMAIL_CACHE = "usersByEmail";
#Cacheable(cacheNames = USERS_BY_MOBILE_CACHE)
Optional<User> findOneByMobile(#Param("mobile") String mobile);
#Cacheable(cacheNames = USERS_BY_EMAIL_CACHE)
Optional<User> findOneByEmailIgnoreCase(#Param("email") String email);
#Cacheable(cacheNames = USERS_BY_LOGIN_CACHE)
Optional<User> findOneByLogin(#Param("login") String login);
Page<User> findAllByLoginNot(Pageable pageable, #Param("login") String login);
List<User> findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime);
}
The Spring Security Configuration is as follows:
package dev.local.gtm.api.config;
import dev.local.gtm.api.security.AuthoritiesConstants;
import dev.local.gtm.api.security.jwt.JWTConfigurer;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
import javax.annotation.PostConstruct;
#RequiredArgsConstructor
#Configuration
#ComponentScan(basePackages = "dev.local.gtm.api")
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Import(SecurityProblemSupport.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationManagerBuilder authenticationManagerBuilder;
private final UserDetailsService userDetailsService;
private final CorsFilter corsFilter;
private final SecurityProblemSupport problemSupport;
private final JWTConfigurer jwtConfigurer;
#PostConstruct
public void init() {
try {
authenticationManagerBuilder
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} catch (Exception e) {
throw new BeanInitializationException("安全配置失败", e);
}
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(HttpMethod.OPTIONS, "/**")
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/index.html")
.antMatchers("/test/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(problemSupport)
.accessDeniedHandler(problemSupport)
.and()
.csrf()
.disable()
.headers()
.frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/**").authenticated()
.antMatchers("/websocket/tracker").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/websocket/**").permitAll()
.antMatchers("/management/health").permitAll()
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
.antMatchers("/v2/api-docs/**").permitAll()
.antMatchers("/swagger-resources/configuration/ui").permitAll()
.antMatchers("/swagger-ui/index.html").permitAll()
.and()
.apply(jwtConfigurer);
}
}
The UserDetailServiceImpl is as follows:
package dev.local.gtm.api.security;
import dev.local.gtm.api.config.Constants;
import dev.local.gtm.api.domain.User;
import dev.local.gtm.api.repository.UserRepo;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Component;
import java.util.Locale;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
#Log4j2
#RequiredArgsConstructor
#Component("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepo userRepo;
#Override
public UserDetails loadUserByUsername(final String login) {
log.debug("正在对用户名为 {} 的用户进行鉴权", login);
if (new EmailValidator().isValid(login, null)) {
val userByEmailFromDatabase = userRepo.findOneByEmailIgnoreCase(login);
return userByEmailFromDatabase.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("系统中不存在 email 为 " + login + " 的用户"));
}
if (Pattern.matches(Constants.MOBILE_REGEX, login)) {
val userByMobileFromDatabase = userRepo.findOneByMobile(login);
return userByMobileFromDatabase.map(user -> createSpringSecurityUser(login, user))
.orElseThrow(() -> new UsernameNotFoundException("系统中不存在手机号为 " + login + " 的用户"));
}
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
val userByLoginFromDatabase = userRepo.findOneByLogin(lowercaseLogin);
return userByLoginFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user))
.orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
}
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
if (!user.isActivated()) {
throw new UserNotActivatedException("用户 " + lowercaseLogin + " 没有激活");
}
val grantedAuthorities = user.getAuthorities().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getName()))
.collect(Collectors.toList());
return new org.springframework.security.core.userdetails.User(user.getLogin(),
user.getPassword(),
grantedAuthorities);
}
}
[update] After I change spring-boot-jest to spring-boot-elasticsearch the error is more specific. it now says the elasticsearchTemplate bean is not defined, but in fact it is.
2018-05-02 17:04:59.776 WARN 76262 --- [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'securityConfig' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/config/SecurityConfig.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsService' defined in file [/Users/wangpeng/workspace/books/gtm/backend/api/out/production/classes/dev/local/gtm/api/security/UserDetailsServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepo': Cannot resolve reference to bean 'elasticsearchTemplate' while setting bean property 'elasticsearchOperations'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'elasticsearchTemplate' available
2018-05-02 17:04:59.805 INFO 76262 --- [ restartedMain] o.apache.catalina.core.StandardService : Stopping service [Tomcat]
2018-05-02 17:04:59.822 INFO 76262 --- [ restartedMain] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2018-05-02 17:04:59.925 ERROR 76262 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in dev.local.gtm.api.security.UserDetailsServiceImpl required a bean named 'elasticsearchTemplate' that could not be found.
Action:
Consider defining a bean named 'elasticsearchTemplate' in your configuration.
Process finished with exit code 1
The bean is defined in ElasticConfig as follows
package dev.local.gtm.api.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.elasticsearch.client.Client;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
#Configuration
#EnableConfigurationProperties(ElasticsearchProperties.class)
#ConditionalOnProperty("spring.data.elasticsearch.cluster-nodes")
public class ElasticConfig {
#Bean
public ElasticsearchTemplate elasticsearchTemplate(Client client, Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
return new ElasticsearchTemplate(client, new CustomEntityMapper(jackson2ObjectMapperBuilder.createXmlMapper(false).build()));
}
public class CustomEntityMapper implements EntityMapper {
private ObjectMapper objectMapper;
public CustomEntityMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
}
#Override
public String mapToString(Object object) throws IOException {
return objectMapper.writeValueAsString(object);
}
#Override
public <T> T mapToObject(String source, Class<T> clazz) throws IOException {
return objectMapper.readValue(source, clazz);
}
}
}
It turns out I reuse same entities for both Elasticsearch and MongoDB, which raise the exception. So I managed to get it resolved by separating the entities
I've used spring security in a Spring Boot application and there are 2 types of users: one is an ADMIN, and one just a simple user. I get the data from a DataSource, then I execute an SQL query.
My problem is with a redirection: for every user I have a different homepage. I'm trying to use to AthenticationSuccessHandler, but it won't work.
Please help.
My Spring security class configuration :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import javax.sql.DataSource;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
Securityhandler successHandler;
// Pour l'authentification des Utilisateur de Table Utilisateur
#Autowired
public void GlobalConfig(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("SELECT \"Pseudo\" AS principal , \"Password\" AS credentials , true FROM \"UTILISATEUR\" WHERE \"Pseudo\" = ? ")
.authoritiesByUsernameQuery("SELECT u.\"Pseudo\" AS principal , r.role as role FROM \"UTILISATEUR\" u ,\"Role\" r where u.id_role=r.id_role AND \"Pseudo\" = ? ")
.rolePrefix("_ROLE");
}
// ne pas appliqué la securité sur les ressources
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/bootstrap/**","/css/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successHandler(successHandler);
}
}
And this is my AuthenticationSuccessHandler:
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
public class Securityhandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_Admin")) {
response.sendRedirect("/admin/home.html");
}
}
}
And this is the error in the console:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name
'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration':
Injection of autowired dependencies failed;
import java.io.IOException;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
#Component
public class Securityhandler implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_ADMIN")) {
response.sendRedirect("admin/home.html");
}
}
}
You've missed the #Component annotation in your success handler class.
Rather than sublcassing AuthenticationSuccessHandler,
It's worth knowing about the Spring security role-checking config:
#Configuration
#EnableWebSecurity
public class SecSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN");
}
...
}
OR pre-checking a Role per endpoint:
#Autowired
#PreAuthorize("hasRole('ADMIN')")
#RequestMapping("/")
public ModelAndView home(HttpServletRequest request) throws Exception {
}
where the default Role prefix is ROLE_
https://docs.spring.io/spring-security/site/docs/3.0.x/reference/el-access.html
https://www.baeldung.com/spring-security-expressions-basic