Spring Security + PostgreSql - How to debug whats going wrong with authentication since there are no error messages? - postgresql

I am following Spring in action book and creating a small demo application to enhance my spring knowledge.
When i tried to setup spring security with PostgreSQL and tried to test authenticated requests, I am always getting 403 forbidden error with no error messages in console log.
I would like to understand whats wrong with the code.
I tried to add debug level for security, introduced AuthenticationEventListener to monitor the events etc. But none of them tell me why authentication fails.
Register controller working fine and its saving user details to DB with encoded password.
For complete code please look into https://github.com/vin0010/Spring-Recipe
SecurityConfig.java
#Configuration
#EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
private UserRepository userRepository;
//TODO check why it didn't work
#Autowired
#Qualifier("name")
private UserDetailsService userDetailsService;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Autowired
public BCryptPasswordEncoder passwordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/test").authenticated()
.antMatchers("/register", "/**").permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.and()
.jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(passwordEncoder);
}
}
TestController for authentication check
#RestController
#RequestMapping("test")
public class TestController {
#GetMapping
public String getDocument(){
return "success";
}
}
Registration controller
#RequestMapping("/register")
public class RegistrationController {
#Autowired
private UserRepository userRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.ACCEPTED)
public void send(#RequestBody RegistrationForm registrationForm) {
userRepository.save(registrationForm.toUser(passwordEncoder));
}
}
UserRepository.java
public interface UserRepository extends CrudRepository<Users, Long> {
Users findByUsername(String username);
}
UserRepositoryUserDetailsService.java
#Service("name")
public class UserRepositoryUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Users users= userRepository.findByUsername(userName);
if (users == null)
throw new UsernameNotFoundException("User '" + userName + "' not found");
return users;
}
}
Users.java
#Entity
#Data
#NoArgsConstructor(access= AccessLevel.PRIVATE, force=true)
#RequiredArgsConstructor
public class Users implements UserDetails {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private final String username;
private final String password;
private final boolean enabled;
private final String role;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Arrays.asList(new SimpleGrantedAuthority(this.role));
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return this.enabled;
}
}
queries for user authentication and authorities check
spring.queries.users-query=select username, password, enabled from users where username=?
spring.queries.roles-query=select username, role from users where username=?
Since I created my own User table, I need to get the authentication done via this en entity setup.

Related

Spring boot login with email password

I was developing a backend with spring boot for the first time. I chose email password login over username password login which is the default behavior of spring boot.
I have two objects: AppUser and User. AppUser is used for authentication and User holds every other data necessary for the application.
AppUser:
public class AppUser implements UserDetails {
#Autowired
AuthRepository repository;
#Id
private String id;
#NonNull
#Indexed(unique = true)
#Field(value = "email")
String email;
#NonNull
#Field(value = "password")
String password;
#Field(value = "authorities")
private List<Role> authorities;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> auth = new HashSet<>();
authorities.forEach(index -> auth.add(new SimpleGrantedAuthority(String.valueOf(index.getRole()))));
return auth;
}
#Override
public String getUsername() {
return email;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
AuthController looks like:
#RestController
#RequestMapping("/api/v1/Auth")
public class AuthController {
Logger logger = LoggerFactory.getLogger(AuthController.class);
#Autowired
UserDetailsManager userDetailsManager;
#Autowired
UserService userService;
#Autowired
AuthService authService;
#Autowired
TokenGenerator tokenGenerator;
#Autowired
DaoAuthenticationProvider daoAuthenticationProvider;
#Autowired
#Qualifier("jwtRefreshTokenAuthProvider")
JwtAuthenticationProvider refreshTokenAuthProvider;
#PostMapping("/SignUp")
public ResponseEntity signup(#RequestBody SignUpRequest signUpRequest) {
logger.info(signUpRequest.getUsername());
User user = new User(
signUpRequest.getEmail(),
signUpRequest.getPassword(),
signUpRequest.getUsername(),
signUpRequest.getFirstName(),
signUpRequest.getLastName(),
signUpRequest.getCountry(),
null,
false,
false
);
AppUser appUser = new AppUser(
signUpRequest.getEmail(),
signUpRequest.getPassword()
);
boolean alreadyExists = authService.existsByEmail(signUpRequest.getEmail());
if(alreadyExists){
return ResponseEntity.status(HttpStatus.CONFLICT).body("Email Already Exists");
}else{
userDetailsManager.createUser(appUser);
User newUser = userService.addUser(user);
Authentication authentication = UsernamePasswordAuthenticationToken.authenticated(appUser, signUpRequest.getPassword(), Collections.EMPTY_LIST);
return ResponseEntity.ok(tokenGenerator.signup(authentication, newUser));
}
}
#PostMapping("/SignIn")
public ResponseEntity signin(#RequestBody SignInRequest signInRequest) {
logger.info(signInRequest.getEmail());
logger.info(signInRequest.getPassword());
Authentication authentication = daoAuthenticationProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(signInRequest.getEmail(), signInRequest.getPassword()));
logger.info(authentication.getCredentials().toString());
User user = new User();
return ResponseEntity.ok(tokenGenerator.signin(authentication, user));
}
}
The issue is with signin. SignUp works fine when I am providing email and password. Login is not working.
I am quite new so have zero idea where the issue is happening. Any reference code with explanation will help a lot.
Github Link
N.B: using spring boot, mongodb, oauth2
In my understanding based on your WebSecurity.class, your are using OAuth2 authentication.
What I would do is create 2 Configurations inside the same class with a different Order and in the second one a custom AuthenticationProvider. This was Spring will try to authenticate first based on the first class and then on the second one
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
//here we set as the first in order the below config if this fails the next one in order will be used until any of them succeeds
#Configuration
#Order(1)
public static class MySecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private MyLoginRequestFilter myLoginRequestFilter ;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http
.antMatchers("/api/v1/Auth/*").permitAll()
.addFilterBefore(myLoginRequestFilter, UsernamePasswordAuthenticationFilter.class )
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.cors()
.disable()
.csrf()
.disable()
;
}
}
Then insert your class below with Order 2 and then close the whole file/class
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Slf4j
#Order(2)
//this is your class that is uploaded in the git. We actually encapsulate inside a generic Security Config class
public class WebSecurity {
#Autowired
JwtToUserConverter jwtToUserConverter;
...
}
} //to close the java class that was started in the previous block of code
You could even add a new function that will handle the HttpSecurity item so that you dont duplicate your code:
public HttpSecurity myHandler(HttpSecurity http) {
return http
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/api/v1/Auth/*").permitAll()
.antMatchers("/api/v1/Home/*").permitAll()
.anyRequest().authenticated()
)
.csrf().disable()
.cors().disable()
;
}
Note that in the Order(1) example, I have added a filter, but you can leave it without a filter and add a custom AuthenticationProvider with inside the class:
#Autowired
private YourAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
YourAuthenticationProvider could look like
#Component
public class YourAuthenticationProvider implements AuthenticationProvider {
#Autowired
AuthService authService; //need to move here the authenticate method from the controller Authentication authentication = daoAuthenticationProvider.authenticate(UsernamePasswordAuthenticationToken.unauthenticated(signInRequest.getEmail(), signInRequest.getPassword()));
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String email = authentication.getName();
final Object credentials = authentication.getCredentials();
if ( credentials == null ) {
return authentication;
}
//I suggest using a password encoder to save the password and then check it with PasswordEncoder (you could create a bean)
final Authentication auth = authService.authenticate(email, credentials.toString());
if (auth != null) {
return auth;
}
throw new BadCredentialsException( "Wrong username and/or password" );
}
}
Hope these help

Spring boot test : How to test a controller with jpa data

I want to test a controller with jpa data.
How can i do it?
I have this controller :
#RestController
#RequestMapping("/api")
public class AuthenticateController {
#Autowired private AuthenticationManager authenticationManager;
#Autowired private ClientRepository client;
#Autowired private JwtUtil jwt;
#Autowired private UserDetailsService details;
#PostMapping("/authenticate") public ResponseEntity<?> createAuthenticationToken(#RequestBody AuthenticationRequest authenticate) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authenticate.getIdentification(), authenticate.getPassword()));
}
catch (BadCredentialsException e) { throw new Exception("Incorrect username or password", e); }
final UserDetails userDetails = details.loadUser(authenticate.getIdentification());
final String jwt = this.jwt.generateToken(userDetails);
final String identification = userDetails.getIdentification();
Client c = client.findByUsernameOrEmailOrPhone(identification, identification, identification).orElseThrow();
return ResponseEntity.ok(new AuthenticationResponse(jwt, c));
}
}
and this jpa data
public interface ClientRepository extends JpaRepository<Client, Integer> {
}

How to registre user in my own database through keycloak

I have been using keycloak for a very short time. made keycloak use my own user database and it works fine.
I would like new users who register to be registered directly in my database. So I implemented the UseRegistrationProvider class which contains the addUser(RealmModel realm, String username) and removeUser(RealmModel realm, UserModel user) methods.
The problem is that in the addUser method I only have the username and I would like to have all the fields that have been filled on the registration form. How do I do?
Thanks
the addUser(...) method return an object implementing the UserModel. You will have to implement a UserModel adapter that enable to set the attributes you want.
See Quickstart example
Regards,
Here is my code. What did i miss?
public class MyUserStorageProvider implements UserStorageProvider,
UserRegistrationProvider,
UserLookupProvider,
UserQueryProvider,
CredentialInputUpdater,
CredentialInputValidator {
private final KeycloakSession session;
private final ComponentModel model;
private final UserRepository repository;
public MyUserStorageProvider(KeycloakSession session, ComponentModel model, UserRepository repository) {
this.session = session;
this.model = model;
this.repository = repository;
}
...
#Override
public UserModel addUser(RealmModel realm, String username) {
User user = new User();
user.setUsername(username);
user.setEmail("I don't have email in addUser method");
if(repository.addUser(user))
return new UserAdapter(session, realm, model, user);
else return null;
}
}
public class UserAdapter extends AbstractUserAdapterFederatedStorage {
private final User user;
private final String keycloakId;
public UserAdapter(KeycloakSession session, RealmModel realm, ComponentModel model, User user) {
super(session, realm, model);
this.user = user;
this.keycloakId = StorageId.keycloakId(model, user.getId());
}
#Override
public String getId() {
return keycloakId;
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public void setUsername(String username) {
user.setUsername(username);
}
#Override
public String getEmail() {
return user.getEmail();
}
#Override
public void setEmail(String email) {
user.setEmail(email);
}
#Override
public String getFirstName() {
return user.getFirstName();
}
#Override
public void setFirstName(String firstName) {
user.setFirstName(firstName);
}
#Override
public String getLastName() {
return user.getLastName();
}
#Override
public void setLastName(String lastName) {
user.setLastName(lastName);
}
}
class UserRepository {
private EntityManager em;
public UserRepository(MultivaluedHashMap<String, String> config) {
em = new JpaEntityManagerFactory(new Class[]{User.class}, config).getEntityManager();
}
...
boolean addUser(User user) {
try {
em.getTransaction().begin();
em.persist(user);
em.getTransaction().commit();
return true;
}catch(Exception e) {
return false;
}
}
}

Configuring AuthenticationManagerBuilder to use User Repository

I am trying to secure Rest APIs using spring boot and JWT. Right now I have been able to piece together pieces of the configuration to get a token generated with a hard coded username and password. I would like my User class and repository to be used instead.
I have been able to hardcode a user here
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user")
.password(passwordEncoder().encode("password"))
.authorities("ROLE_USER");
}
Should I be pointing this to my UserDetailsService? How would I do that?
#Service
public class UserSecurityService implements UserDetailsService {
private static final Logger LOG = LoggerFactory.getLogger(UserSecurityService.class);
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername (String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (null == user) {
LOG.warn("username not found");
throw new UsernameNotFoundException("Username" + username + "not found");
}
return user;
}
}
For UserDetailsService, you need DaoAuthenticationProvider to handle any authentication requests.
To do so:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(encoder());
}
// you shouldn't use plain text
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
above internally configures a DaoAuthenticationProvider. Alternatively, you can define a bean to inject:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}

Spring Boot store data with WebSockets

I have a simple WebSocket set up and try to save data. Somehow the data gets not persisted. I don't get any error messages and the object gets returned correct to the client. If I try to store the object with a REST controller and a REST request it works.
Here are the dependencies of my build.gradle file:
dependencies {
compile 'org.springframework.boot:spring-boot-starter-web'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'org.springframework.boot:spring-boot-starter-websocket'
compile 'org.springframework:spring-messaging'
compile 'com.google.code.gson:gson:1.7.2'
compile 'org.postgresql:postgresql:9.4-1200-jdbc41'
compile 'commons-dbcp:commons-dbcp:1.4'
testCompile('org.springframework.boot:spring-boot-startet-test')
}
PersonController
#Controller
public class PersonController {
#Autowired
PersonRepository personRepository;
#MessageMapping("/test")
#SendTo("/response/test")
public Person test() throws Exception {
Person person = new Person();
person.setName("John Doe");
return personRepository.save(person);
}
}
Configuration for STOMP messaging
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/response");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS();
}
Person entity
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return getName;
}
public void setName(String name) {
this.name = name;
}
}
Base Repository
#NoRepositoryBean
public interface BaseRepository<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
void delete(ID id);
Iterable<T> findAll();
T findOne(ID id);
T save(T persisted);
Iterable<T> save(Iterable<T> persited);
}
Person Repository
public interface PersonRepository extends
BaseRepository<Person, Serializable> {
}
Is there a problem in my code?
Is there an issue with caching? Do I have to force flushing?
Is storing data with WebSockets supported by SpringBoot?
Do you know any examples with storing data? I could only find basic examples without storing data.
The problem was in my persistence configuration. I changed the configuration from a Java implementation to the application.properties file. I think there was a problem with my transaction manager.
To be complete, here is my current application.properties file:
spring.datasource.url = jdbc:postgresql://localhost:5432/test
spring.datasource.username = test
spring.datasource.password = test
spring.datasource.testWhileIdle = true
spring.datasource.validationQuery = SELECT 1
spring.jpa.show-sql = true
spring.jpa.hibernate.ddl-auto = create
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect