Access is denied when user login as 'ADMIN' role but not in 'USER' role - spring-boot

WebSecurityConfiguration
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private MyUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
String loginPage = "/login";
String logoutPage = "/logout";
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers(loginPage).permitAll()
//.antMatchers("/registration").permitAll()
.antMatchers("/user/**").hasAnyAuthority("USER","ADMIN")
.anyRequest().authenticated()
.and()
.csrf().disable()
.formLogin()
.loginPage(loginPage)
.loginPage("/")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/user")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher(logoutPage))
.logoutSuccessUrl(loginPage).and().exceptionHandling();
}
}
MyUserDetailsService
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
#Override
#Transactional
public UserDetails loadUserByUsername(String userName) {
User user = userService.findUserByUserName(userName);
List<GrantedAuthority> authorities = getUserAuthority(user.getRoles());
return buildUserForAuthentication(user, authorities);
}
private List<GrantedAuthority> getUserAuthority(Set<Role> userRoles) {
Set<GrantedAuthority> roles = new HashSet<>();
for (Role role : userRoles) {
roles.add(new SimpleGrantedAuthority(role.getRole()));
}
return new ArrayList<>(roles);
}
private UserDetails buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(),
user.getActive(), true, true, true, authorities);
}
}
Model
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private long id;
#Column(name = "username")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "active")
private Boolean active;
#ManyToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "user_role", joinColumns = #JoinColumn(name = "user_id"), inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles;
// getters and setters
}
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "role_id")
private int id;
#Column(name = "role")
private String role;
// getters and setters
}
I got error Spring Boot security login. I got error in admin role login. But when I logged in user role it is working well. I don't understand why this is happening?
When I am trying to login as admin it redirects me http://localhost:8080/error page show this in body:
{"timestamp":"2020-09-11T14:10:05.108+00:00","status":999,"error":"None","message":""}
But when trying to login as user it works fine.

Probably you may have messed up with antMatchers!!
In your code you've mentioned like
.antMatchers("/user/**").hasAnyAuthority("USER","ADMIN")
Which means both user and admin can have access to url's that match "/user/**" ie,. all url's with prefix "/user/"
If incase you have a url for admin which with "/admin/*
" , then you should give access to "/admin/**" too!
Then you have to add one more antMatcher like
.antMatchers("/admin/**).hasRole("ADMIN")
which will give access only to admin url's ie,. with prefix "/admin/**"

Related

#AuthenticationPrincipal returns null

I setup my Spring Security application according to the reference document and after hours of troubleshooting I continue to get a null #AuthenticationPrincipal passed into my controller.
The authentication mechanism is working fine against the users in my database but still a null #AuthenticationPrincipal. I consulted several internet posts but still I am getting null.
WebSecurityConfig:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider provider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userService);
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/registration").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider());
}
}
Message (entity):
#Entity
#Table(name = "sweater_message")
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String text;
private String tag;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User author;
public Message(String text, String tag, User user) {
this.author = user;
this.text = text;
this.tag = tag;
}
public Message() {
}
...getters and setters
User(entity):
#Entity
#Table(name = "sweater_user")
public class User implements UserDetails {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private boolean active;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(
name = "sweater_user_role",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id")
)
private Collection<Role> roles;
public User(String username, String password, boolean active, Collection<Role> roles) {
this.username = username;
this.password = password;
this.active = active;
this.roles = roles;
}
public User() {
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return isActive();
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList());
...getters and setters
}
UserService
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
public User save(User user) {
User saveUser = new User(
user.getUsername(),
new BCryptPasswordEncoder().encode(user.getPassword()),
true,
Arrays.asList(new Role("USER")));
return userRepository.save(saveUser);
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User findUser = userRepository.findByUsername(username);
if (findUser == null) {
throw new UsernameNotFoundException("There is no user with this username");
}
return new org.springframework.security.core.userdetails.User(
findUser.getUsername(),
findUser.getPassword(),
mapRolesToAuthorities(findUser.getRoles()));
}
public Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles) {
return roles.stream().map(role -> new SimpleGrantedAuthority(role.getName())).collect(Collectors.toSet());
}
}
Controller:
#PostMapping("/main")
public String add(
#AuthenticationPrincipal User user,
#RequestParam String text,
#RequestParam String tag,
Map<String, Object> model
){
...user is null
}
try Changing #AuthenticationPrincipal User user to #AuthenticationPrincipal UserDetails userDetails since loadUserByUsername returns UserDetails
Using SecurityContextHolder.getContext().getAuthentication().getPrincipal() in your controller to see whether your userdetails object is stored in the right place since #AuthenticationPrincipal is an abbreviation for (UserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal().
For example:
#GetMapping("/all")
public ResponseEntity<String> test(#AuthenticationPrincipal AuthUserDetails userDetails) {
System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
return ResponseEntity.ok("success");
}
If the out print is something other than the UserDetails object, it means that you did not set Principal correctly when you are initializing your Authentication in the filter class. Let's use UsernamePasswordAuthenticationToken as an example:
// in filter class
#Component
public class JwtFilter extends OncePerRequestFilter {
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Optional<AuthUserDetails> authUserDetailOptional = jwtProvider.resolveToken(request); // extract jwt from request, generate a userdetails object
if (authUserDetailOptional.isPresent()){
AuthUserDetails authUserDetails = authUserDetailOptional.get();
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
authUserDetails, // set your authUserDetails here!!
null,
authUserDetails.getAuthorities()
); // generate authentication object
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "The token is not valid.");
}
}

Spring Security cannot authenticate user although the user is exist in database

please help me with this, I'm new to spring security and I have been trying to logged in but Spring Security just don't let me access and I still can't figure. My CustomUserDetailsService still working and print out the account I intend to use to login
SecurityConfig
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
BCryptPasswordEncoder passwordEncoder;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
// TODO Auto-generated method stub
return super.authenticationManager();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/logout").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.defaultSuccessUrl("/admin", true)
.and()
.exceptionHandling().accessDeniedPage("/accessDenied");
}
}
CustomUserDetailsService
#Service("customUserDetailsService")
#Transactional
#Slf4j
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private RoleRepository roleRepository;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findUsersByUsername(username);
if (user == null) {
log.error("User not found");
throw new UsernameNotFoundException("User not found");
} else {
log.info("User found in the dbs", username);
System.out.println(user.getUsername());
System.out.println(user.getPassword());
}
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
//looping all roles from user -> for each role, create a new simpleGranted
//auth by passing the role name
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
//return spring sec user (core userDetail)
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
}
User
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Table(name = "user",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
#UniqueConstraint(columnNames = "email")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank(message = "Username is required")
private String username;
#NotBlank(message = "Password is required")
private String password;
#Email
#NotBlank(message = "Email is required")
private String email;
private Instant created;
private boolean enabled;
//load all the roles whenever load an user
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Role> roles = new ArrayList<>();
}
Everytime I logged in with the right account, Spring Security always give me "Bad Credentials"
Edited: username and password (both passwords are 123)
It isn't working because you didn't specify the implementation of the UserDetailsService.So what spring is actually doing is, It is using the default username(user) and the random password(generated at runtime) as the required credentials. To make spring use your custom user details, please replace
This:
#Autowired
private UserDetailsService userDetailsService;
With that:
#Autowired
#Qualifier("customUserDetailsService")
private UserDetailsService userDetailsService;

Encoded password does not look like BCrypt: authentication customization against a Relational Database - Spring Security

I'm using Spring Boot 2.2.5.RELEASE.
I've researched a lot of examples on stackoverflow and other resources, but nothing helps me and I couldn't login to use h2-console because of warning:
2020-03-31 18:22:57.545 WARN 29312 --- [nio-8080-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
My configuration class was like:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration
extends WebSecurityConfigurerAdapter {
private final UserInfoDetailsService userInfoDetailsService;
private BCryptPasswordEncoder passwordEncoder;
private DataSource dataSource;
public SpringSecurityConfiguration(UserInfoDetailsService userInfoDetailsService) {
this.userInfoDetailsService = userInfoDetailsService;
this.dataSource = new Jdbc3PoolingDataSource();
this.passwordEncoder = new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userInfoDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder);
return authenticationProvider;
}
#Override
protected void configure(
AuthenticationManagerBuilder authenticationManagerBuilder)
throws Exception {
authenticationManagerBuilder
.userDetailsService(userInfoDetailsService)
.passwordEncoder(passwordEncoder)
.and()
.authenticationProvider(authenticationProvider())
.jdbcAuthentication()
.dataSource(dataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/user/**", "/h2-console/**").hasRole("ADMIN").anyRequest()
.authenticated()
.and()
.httpBasic()
.realmName("User Registration System")
.and()
.csrf()
.disable();
}
}
UserInfo domain object was like:
#Entity
#Table(name = "users")
public class UserInfo {
#Id
#GeneratedValue
#Column(name = "id")
private Long id;
#Column(name = "username")
#NotEmpty
private String username;
#Column(name = "password")
#NotEmpty
private String password;
#Column(name = "enabled")
private boolean isEnabled;
#Column(name = "role")
private String role;
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isEnabled() {
return isEnabled;
}
public void setEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
And UserInfoDetailsService class was like:
#Service
public class UserInfoDetailsService implements UserDetailsService {
private final UserInfoJpaRepository userInfoJpaRepository;
public UserInfoDetailsService(UserInfoJpaRepository userInfoJpaRepository) {
this.userInfoJpaRepository = userInfoJpaRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserInfo user = userInfoJpaRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(
"Opps! user not found with user-name: " + username);
}
return new User(
user.getUsername(), user.getPassword(),
Collections.singleton(getAuthorities(user)));
}
private SimpleGrantedAuthority getAuthorities(UserInfo user) {
return new SimpleGrantedAuthority("ROLE_" + user.getRole());
}
}
Moreover, I've generated some test users:
INSERT INTO users (id, username, password, enabled, role) VALUES (1, 'user', 'password', true, 'USER');
INSERT INTO users (id, username, password, enabled, role) VALUES (2, 'admin', 'password', true, 'ADMIN');
INSERT INTO users (id, username, password, enabled, role) VALUES (3, 'users2', 'password', true, 'USER');
To identify this problem "Encoded password does not look like BCrypt" I setup a break point in class org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder. And then checked the root cause for the warning.
So, the cause of warning was because of open passwords I've inserted:
To solve it, I've encoded password and updated users table:
INSERT INTO users (id, username, password, enabled, role) VALUES (1, 'user', '$2a...', true, 'USER');
INSERT INTO users (id, username, password, enabled, role) VALUES (2, 'admin', '$2a...', true, 'ADMIN');
INSERT INTO users (id, username, password, enabled, role) VALUES (3, 'users2', '$2a...', true, 'USER');

Oauth 2: Spring Boot 2: Auth Server /oauth/check_token returns user_name as null

I have implemented 2 distinct servers: auth server and a resource server using Spring Boot 2.1.6.RELEASE and spring-cloud-starter-oauth2 version Greenwich.RELEASE
I am able to successfully get an access_token from auth server, use it to access protected api on resource server.
However I am not able to get user_name in the response returned by auth server's /oauth/check_token endpoint I can confirm that user_name is present in user table.
curl http://localhost:5000/oauth/check_token?token=a3ee84ee-6d3a-4a8f-af19-5446b55c637f | jq .
returns following:
{
"aud": [
"article"
],
"user_name": null,
"scope": [
"READ",
"WRITE",
"UPDATE",
"DELETE"
],
"active": true,
"exp": 1563849438,
"authorities": [
"ROLE_administrator",
"create_article",
"read_article",
"delete_article",
"update_article"
],
"client_id": "myclient"
}
AuthorizationServerConfiguration
#Configuration
public class AuthorizationServerConfiguration implements AuthorizationServerConfigurer {
private PasswordEncoder passwordEncoder;
private DataSource dataSource;
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
public AuthorizationServerConfiguration(
PasswordEncoder passwordEncoder,
DataSource dataSource,
AuthenticationManager authenticationManager) {
this.passwordEncoder = passwordEncoder;
this.dataSource = dataSource;
this.authenticationManager = authenticationManager;
}
#Bean
TokenStore jdbcTokenStore() {
return new JdbcTokenStore(dataSource);
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
// security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
security.checkTokenAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(jdbcTokenStore());
endpoints.authenticationManager(authenticationManager);
//TODO JWT
// endpoints.accessTokenConverter(accessTokenConverter());
}
}
WebSecurityConfiguration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/version").permitAll()
.antMatchers("/api/**").authenticated();
}
}
UserDetailsServiceImpl
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository userRepository;
#Autowired
public UserDetailsServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
Optional<User> optionalUser = userRepository.findByUserName(userName);
optionalUser.orElseThrow(() -> new UsernameNotFoundException("Username or password wrong"));
UserDetails userDetails = new AuthUserDetail(optionalUser.get());
new AccountStatusUserDetailsChecker().check(userDetails);
return userDetails;
}
}
Main application class
#SpringBootApplication
#EnableAuthorizationServer
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
AuthUserDetail
package com.myapplication.models;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class AuthUserDetail extends User implements UserDetails {
public AuthUserDetail(User user) {
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
getRoles().forEach(role -> {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
role.getPermissions().forEach(permission -> {
grantedAuthorities.add(new SimpleGrantedAuthority(permission.getName()));
});
});
return grantedAuthorities;
}
#Override
public String getPassword() {
return super.getPassword();
}
#Override
public String getUsername() {
return super.getUserName();
}
#Override
public boolean isAccountNonExpired() {
return super.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return super.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return super.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return super.isEnabled();
}
}
User
package com.myapplication.models;
import lombok.Data;
import javax.persistence.*;
import java.util.List;
#Entity
#Table(name = "user")
#Data
public class User {
public User() {
}
public User(User user) {
this.userName = user.getUserName();
this.password = user.getPassword();
this.email = user.getEmail();
this.enabled = user.isEnabled();
this.accountNonExpired = user.isAccountNonExpired();
this.credentialsNonExpired = user.isCredentialsNonExpired();
this.accountNonLocked = user.isAccountNonLocked();
this.roles = user.getRoles();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "user_name")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "enabled")
private boolean enabled;
#Column(name = "account_non_expired")
private boolean accountNonExpired;
#Column(name = "credentials_non_expired")
private boolean credentialsNonExpired;
#Column(name = "account_non_locked")
private boolean accountNonLocked;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "role_user", joinColumns = {#JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {
#JoinColumn(name = "role_id", referencedColumnName = "id")})
private List<Role> roles;
}
user table ddl
create table if not exists user
(
id int auto_increment
primary key,
user_name varchar(100) not null,
password varchar(1024) not null,
email varchar(1024) not null,
enabled tinyint not null,
account_non_expired tinyint not null,
credentials_non_expired tinyint not null,
account_non_locked tinyint not null,
constraint user_name
unique (user_name)
);
I got the same problem after I modified column names of my user table from camelCase to underscore_case.
To resolve this I made sure the user, permission and role class is implementing Serializable class
#Entity
#Table(name = "user")
public class User implements Serializable {
----
----
}
and also remove the following spring jpa property if added
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Spring Security basic auth always getting 401

I learning Spring, and I integrated Spring security into my current APIs. To keep things simple, I am starting with Basic Auth.
However, the issue that I am facing is that, if I don't provide the credentials, I get the standard 401 along with a JSON response:
{
"timestamp": "2018-07-07T18:40:00.752+0000",
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/courses"
}
But if I do pass correct credentials, I get 401, but without any response body.
Here's my WebSecurityConfiguration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DetailsService detailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsService)
.passwordEncoder(User.encoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable();
}
}
Here's my DetailsService:
#Component
public class DetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByEmail(username);
if (user == null) {
throw new UsernameNotFoundException("User with email " + username + " was not found");
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
AuthorityUtils.createAuthorityList(user.getRoles())
);
}
}
I should point this out that I am looking up user by email instead of username.
Here's my user entity:
#Entity
#Table(name = "users")
public class User extends BaseEntity {
public static final PasswordEncoder encoder = new BCryptPasswordEncoder();
#Column(name = "first_name")
private String firstName;
#JoinColumn(name = "last_name")
private String lastName;
private String email;
#JsonIgnore
private String password;
#JsonIgnore
private String[] roles;
public User(String email, String firstName, String lastName, String password,
String[] roles) {
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
setPassword(password);
this.roles = roles;
}
// getters and setters
}
First: check that user.getRoles() is not throwing a LazyInitializationException
Second: if hash has been generated online, BCryptPasswordEncoder might not work

Resources