I want to use both in memory auth and userDetailsService auth. Beacause I have to create one admin account when application start and then I will create another one with form. But the first one had to in memory due to security reason.
SecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
/* #formatter:off */
http.authorizeRequests()
// some configs
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(this.userDetailsService)
.passwordEncoder(passwordEncoder())
.and()
.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user").password("1").roles("ADMIN");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserDetailsServiceImpl
#Service
#Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private VendorService vendorService;
#Autowired
private AdminService adminService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userAdmin = adminService.getAdminByUsername(username);
if (userAdmin != null) {
System.out.println(userAdmin.getUsername());
return userAdmin;
} else {
UserDetails userVendor = vendorService.getVendorByUsername(username);
if (userVendor == null) {
throw new UsernameNotFoundException("No such user");
}else{
System.out.println(userVendor.getUsername());
return userVendor;
}
}
}
}
I can login with in DB users but can't login with user and 1 password.
Related
I have spring web mvc project with Spring Security 4.1.0.RELEASE
In spring controller i try fetch the user from the context
#RestController
public class Test {
#RequestMapping(value="test", method = RequestMethod.POST)
public ResponseEntity<Void> test() {
ContextUser user = (ContextUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
user is an instance with id = 0, login = null .... It is empty instance.
org.springframework.security.core.Authentication isAuthenticated = true, list of Collection<? extends GrantedAuthority> are correct This behaviour is occurred periodically. It is not every time for this request. I catch this issue only for that request
My configurations
#Configuration
#ComponentScan(basePackages={"com.test.app"},
excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.test.app.web.*"}))
#PropertySource(value = { "classpath:application.properties" })
#EnableAspectJAutoProxy
public class AppConfig {
#Autowired
private DataSource dataSource;
//My beans
}
#Component
public class TestUserDetailsService implements UserDetailsService{
#Autowired
private TestUserService service;
#Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
User user = service.findByLogin(userName);
if (user == null) {
throw new UsernameNotFoundException("Error");
}
return new ContextUser(user);
}
}
public class ContextUser extends User implements UserDetails {
//...
}
#Configuration
#EnableWebSecurity
#EnableAsync
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private TestAuthenticationEntryPoint testAuthenticationEntryPoint;
#Autowired
private TestSimpleUrlAuthenticationSuccessHandler testSimpleUrlAuthenticationSuccessHandler;
#Autowired
private TestSimpleUrlAuthenticationFailureHandler testSimpleUrlAuthenticationFailureHandler;
#Autowired
private LogoutSuccessHandler logoutSuccessHandler;
#Autowired
private TestUserDetailsService testUserDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder registry) throws MyException {
registry.userDetailsService(testUserDetailsService).passwordEncoder(new TestEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(testAuthenticationEntryPoint)
.and().sessionManagement().sessionFixation().migrateSession().maximumSessions(-1).sessionRegistry(sessionRegistry()).and()
.and()
.authorizeRequests()
.antMatchers("/").access("hasAuthority('TEST')")
.anyRequest().authenticated()
.and()
.formLogin()
.successHandler(testSimpleUrlAuthenticationSuccessHandler)
.failureHandler(testSimpleUrlAuthenticationFailureHandler)
.and()
.logout().logoutSuccessHandler(logoutSuccessHandler)
.and()
.headers().cacheControl().disable().frameOptions().sameOrigin();
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/resources/**");
}
}
Are any ideas why this behaviour is happened?
I am working on authentication service part of cloud app and I created the following security config class.
#Configuration
#EnableWebSecurity
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {
private final PasswordEncoder encoder;
private final UserService userService;
private final JwtConstant jwtConstant;
#Autowired
public JwtSecurityConfig(PasswordEncoder encoder, UserService userService, JwtConstant jwtConstant) {
this.encoder= encoder;
this.userService = userService;
this.jwtConstant = jwtConstant;
}
#Bean
public DaoAuthenticationProvider getAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(encoder);
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(getAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(getAuthenticationFilter())
.authorizeRequests()
.antMatchers(HttpMethod.PUT, "/signup").permitAll()
.anyRequest()
.authenticated();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
return new AuthenticationFilter(authenticationManager(), jwtConstant);
}
}
I am not sure about the chain methods of configure(HttpSecurity http) method. The authentication service will only receive "login" and "signup" requests.
Should I remove authorizeRequests() method as I do not authorize anything?
I am not sure about anyRequest().authenticated() part either if I really need it?
there are a couple of things that have to be changed, but first of all, you have to define a method that will provide jwt for each request and every request should provide an AuthRequest object that contains username and password :
#RestController
public class WelcomeController {
#Autowired
private JwtUtil jwtUtil;
#Autowired
private AuthenticationManager authenticationManager;
#PostMapping("/signup")
public String generateToken(#RequestBody AuthRequest authRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUserName(), authRequest.getPassword())
);
} catch (Exception ex) {
throw new Exception("inavalid username/password");
}
return jwtUtil.generateToken(authRequest.getUserName());
}
}
and in the UserDetailsService you can make authentication as below :
#Service
public class UserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
#Autowired
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("tried to loging : " + username);
if(!Objects.isNull(username) && !"".equals(username)){
Optional<User> user = userRepository.findUserByUserName(username);
System.out.println(user.get());
if(user.isPresent()){
User userParam = user.get();
return new org.springframework.security.core.userdetails.User(userParam.getUserName(),
userParam.getPassword(), new ArrayList<>());
}
}
throw new UsernameNotFoundException("user does not exists or empty !!");
}
}
and for the configuration side :
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final UserDetailsService userDetailsService;
#Autowired
private final JwtFilter jwtFilter;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers("/signup").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling().and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);;
}
}
for further information, you can follow my Github branch Authnticaition sample
I struggle to understand how to test the Spring Boot application with the Spring Security layer.
I have asked similar questions How to enforce Spring Security with MockitoJUnit runner? and How to mock customer user service details in Spring Security layer?
I have a custom implementation for UserDetailsService which take data from the database, so naturally, I want to take it from somewhere else during unit-testing phase
I want to test my web-layer to see if Spring Security works and if all other use-cases work as well.
How can I achieve both tasks above?
SecurityConfig class:
#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);
}
}
CustomUserDetailsService class:
#Service("customUserDetailsService")
#Profile("!test")
public class CustomUserDetailsService implements UserDetailsService {
private final static Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
private StringRedisTemplate redisTemplate;
private RedisProperties redisProperties;
#Autowired
public CustomUserDetailsService(RedisProperties redisProperties, StringRedisTemplate redisTemplate) {
this.redisProperties = redisProperties;
this.redisTemplate = redisTemplate;
}
#Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
// Do queries to DB and return user if such user exists
}
}
I also added CustomUserDetailsServiceTest class for the unit-testing purpose only into the same package but under src/test/java:
#Service("customUserDetailsService")
#Profile("test")
public class CustomUserDetailsServiceTest implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
#SuppressWarnings("serial")
Map<Object, Object> entries = new HashMap<Object, Object>(){{
put("username", username);
put("password_hash", "$2a$08$YjMcOsLmbbUB4DYPxqSAxOa3STLjEDovwd2..Uidwp.asyhSi8Y5u");
put("role", "ADMIN");
}};
UserEntity user = new UserEntity(entries);
return new HiccasoftApiUser(user);
}
}
How can I use custom implementation for customUserDetailsService in unit-tests while testing web-layer?
The problem is, BCryptPasswordEncoder is not encrypt the password on login process therefore login fails, let's say password is 123 and stored in db as hashed, when the post the password 123 returns invalid_grants, but when the hashed password is sent from client, returns the access token. It is also ok when the password when password encoder is commented.
App.java
#SpringBootApplication
public class App {
#Bean
BCryptPasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) throws Exception {
SpringApplication.run(App.class, args);
}
#Autowired
public void authenticationManager(AuthenticationManagerBuilder authenticationManagerBuilder, final UserRepository userRepository, UserService userService) throws Exception {
if(userRepository.count() == 0) {
User user = new User();
Role role = new Role();
role.setName("SA");
user.setEmail("test");
user.setPassword("123");
user.setRoles(Arrays.asList(role));
user.setBlocked(false);
user.setEnable(true);
userService.save(user);
}
authenticationManagerBuilder.userDetailsService(email -> {
return userService.loadUserByUsername(email);
});
}
}
WebSecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
static final String SIGNING_KEY = "kKSMJ92Mknk38njs9HJ8KNALiuc938FH";
static final int ENCODING_STRENGTH = 256;
static final String SECURITY_REALM = "Task Manager";
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder)
.and()
.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/sign-up", "/sign-in", "/").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic()
.realmName(SECURITY_REALM)
.and()
.csrf()
.disable();
}
#Bean
public UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
UserService.java
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
public void save(User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));;
userRepository.save(user);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findUserByEmail(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return new CustomUserDetails(user);
}
}
The problem might be here. Remove the lines that are commented out.
#Autowired
public void authenticationManager(AuthenticationManagerBuilder authenticationManagerBuilder, final UserRepository userRepository, UserService userService) throws Exception {
if(userRepository.count() == 0) {
User user = new User();
Role role = new Role();
role.setName("SA");
user.setEmail("test");
user.setPassword("123");
user.setRoles(Arrays.asList(role));
user.setBlocked(false);
user.setEnable(true);
userService.save(user);
}
// authenticationManagerBuilder.userDetailsService(email -> {
// return userService.loadUserByUsername(email);
// });
}
You are overriding all the multiple configurations that you have done in your configuration classes, and as a result the password encoder is never applied to your AuthenticationManagerBuilder.
I'm working on with spring boot security layer to authenticate and authorize the user.Now, i would like to do some sample app using multi http security configuration.I have the scenario like there will be two login pages with different URL mappings("/managementLogin","/othersLogin").
I can understood how to configure multi httpsecurity configs but i need to validate the users from two tables.If the management users loggedIn i need to validate the user from management table through DAO layer using UserDetailsService else if any other users loggedIn i need to validate from other_users table.
Could anybody help me to know how to configure the multi http config and dao layer using UserDetailsService with spring boot security ?
Here is my basic code snippet,
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
UserDetailsService userDetailsService;
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// for testing authentication purpose using inMemory db
/*
* auth.inMemoryAuthentication().withUser("user").password("user").roles
* ("USER").and().withUser("admin") .password("admin").roles("ADMIN");
*/
// Dao based authentication
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home").permitAll();
http.authorizeRequests().antMatchers("/rest/**").authenticated();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutSuccessUrl("/");
// CSRF tokens handling
http.addFilterAfter(new CsrfTokenResponseHeaderBindingFilter(), CsrfFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/registerUser","/register.html");
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
TIA..,
Implement a custom UserDetailsService like this:
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserDaoTableOne userDaoTableOne;
#Autowired
private UserDaoTableTwo userDaoTableTwo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = userDaoTableOne.find(username);
if(user == null){
user = userDaoTableTwo.find(username);
}
if (user == null) {
throw new UsernameNotFoundException(String.format("Username '%s' not found", username));
}
return user;
}
}
Implement two DaoAuthenticationProvider with his own UserDetailsService and inject both providers to the authenticationManager.
I don't know what is the requisite for two distinct login endpoints but at first I think is a bad idea.
You can create different Authentication objects an let the AuthenticationManager choose the correct AuthenticationProvider based in the supports method.
Indeed you will need to use, two user detail services. But, that wont be enough. I suggest you to create another ApplicationSecurity2 class with different order.
Spring security is built on an ordered list of filter chains.
see the answer given here by Dave Sayer. Then you can handle different urls, as you want.
in my case I checked into two repositories, Below an exemple that I use:
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AbstractUser user;
try {
user = clientRepository.findByUsername(username);
}
catch (Exception userException) {
try {
user = adminRepository.findByUsername(username);
}
catch (Exception adminException) {
throw new UsernameNotFoundException("No user present with username : " + username);
}
}
return user;
}
I have to handle around same issue , i have autowired httprequest class in userdetail service and get request params type and drive my logic based on that.
you can directly solve the issue as the recommended solutions, but you can create a simple trick to define two different UserDetailsService as here I have two user one as a normal user and another as an editor :
editor
#Log4j2
#RequiredArgsConstructor
#Service
public class EditorService implements UserDetailsService {
private final EditorRepository editorRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username == null || "".equals(username)){
throw new UsernameNotFoundException("null value");
}
Optional<Editor> editor = editorRepository.findByUsername(username);
if(editor.isPresent()){
log.info("created under editor service: " + editor.get());
return editor.get();
}
throw new UsernameNotFoundException("does not exists");
}
}
user
#Log4j2
#RequiredArgsConstructor
#Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username == null || "".equals(username)){
throw new UsernameNotFoundException("null");
}
Optional<User> user = userRepository.findByUsername(username);
if(user.isPresent()){
log.info("cretaed under User service : " + user.get());
return user.get();
}
throw new UsernameNotFoundException("does not exists");
}
}
then on the configurations side, we can use of spring order mechanism :
user config :
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
#Order(1)
public class UserWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/user/**")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder(10);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userService).passwordEncoder(passwordEncoder());
}
}
Editor config :
#EnableWebSecurity
#Configuration
#RequiredArgsConstructor
public class EditorWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final EditorService editorService;
#Lazy
private final PasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http // all other requests handled here
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.editorService).passwordEncoder(passwordEncoder);
}
}