Spring boot security | Why does my authenticationManager not work? - java

So I am making my first spring boot project (I am making a RESTful backend API) and I came across jwt authentication. After trying a few tutorials I was keep getting stuck until one tutorial kinda helped me.
My problem:
When I run my AuthenticationController:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
My code does not run past this piece of code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
I know this because of debugging basically. It always goes to the catch and I don't know why.
When I comment out that not working piece of code, my code runs but I can use any username and password with the given jwt Token, and that is not what I want.
So I know the piece of code that makes sure my program does not run as planned is that piece.
I know the not working piece of code uses my JwtRequest so maybe anyone needs that to help me fix my problem:
JwtRequest:
package com.example.demo.Model;
public class JwtRequest{
private String username;
private String password;
public JwtRequest() {
}
public JwtRequest(String username, String password) {
this.username = username;
this.password = password;
}
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;
}
}
Here is my debugger and database to confirm that the credentials are correct:
My debugger:
My DataBase:
My Postman:
What I have tried:
I have tried replacing this code:
try {
authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
);} catch (BadCredentialsException e){
throw new Exception("Incorrect username or password", e);
}
With this:
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
And my according authenticate method:
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
But then my debugger says this:
I am basically stuck and I do not know what to do now. Can anyone help me or provide me with a tutorial on how to implement JWT authentication with an MYSQL database that is not outdated and has the source code included in the tutorial?
Thanks!
If you need some piece of code just ask and I will include it in the post.
As requested the following files:
WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
Password encoder:
#Configuration
public class PasswordEncoder{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
UserdetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}

#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
You are setting accountService as userDetailsService instead of `JwtUserDetailsService'. have you tried that?
change in JwtUserDetailsService.java
//not sure why accountService-> don't make it final
//#Autowired //missing
//private AccountService accountService;
#Autowired
private PasswordEncoder bcryptEncoder;
#Configuration
#AllArgsConstructor
//#EnableWebSecurity not needed
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
//private AccountService accountService;
#Autowired
private PasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); You are not using filter to authenticate, you are using a controller for that
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
//provider.setUserDetailsService(accountService);
return provider;
}
}

Related

Add database authentication to Spring Data Rest application

I'm creating an application using Spring Data REST with Thymeleaf.
Initially I created my models, controllers, dao and services. All worked fine. I'm now trying to add security to my application. Right now I'm just focused on the login/logout.
I've been able to create an in memory authentication as below:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("securityDataSource")
private DataSource securityDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// add users for in memory authentication
UserBuilder users = User.withDefaultPasswordEncoder();
auth.inMemoryAuthentication()
.withUser(users.username("paul").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("sandra").password("test123").roles("MEMBER", "ADMIN"))
.withUser(users.username("matthew").password("test123").roles("MEMBER"));
}
}
I want to change this to database authentication though. I'm pretty sure I can create a jdbc connection and change my config method to something like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(securityDataSource);
}
My problem is that I'm already accessing the database through my DAO interfaces. E.g:
public interface UserRepository extends JpaRepository<User, Integer> {
// method to sort by last name
public List<User> findAllByOrderByLastNameAsc();
}
My users table has an email and password column which will be used as the username/password.
Is it possible to also authenticate by using this in some way? I can provide additional information but am reluctant to just post everything and hope somebody will write it for me.
Since you've already created the DAO interfaces, it may be easier to create
a UserDetailsService implementation:
#Service
#NoArgsConstructor #ToString #Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private UserRepository userRepository = null;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
org.springframework.security.core.userdetails.User user = null;
try {
Optional<User> optional = userRepository.findBy...(username);
HashSet<GrantedAuthority> set = new HashSet<>();
/*
* Add SimpleGrantedAuthority to set as appropriate
*/
user = new org.springframework.security.core.userdetails.User(username, optional.get().getPassword(), set);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
and wire it in with:
#Autowired private UserDetailsService userDetailsService = null;
... private PasswordEncoder passwordEncoder = ...;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
For some additional clarity, here is the complete context of my implementation:
#Service
#NoArgsConstructor #ToString #Log4j2
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired private CredentialRepository credentialRepository = null;
#Autowired private AuthorityRepository authorityRepository = null;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = null;
try {
Optional<Credential> credential = credentialRepository.findById(username);
Optional<Authority> authority = authorityRepository.findById(username);
HashSet<GrantedAuthority> set = new HashSet<>();
if (authority.isPresent()) {
authority.get().getGrants().stream()
.map(Authorities::name)
.map(SimpleGrantedAuthority::new)
.forEach(set::add);
}
user = new User(username, credential.get().getPassword(), set);
} catch (UsernameNotFoundException exception) {
throw exception;
} catch (Exception exception) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
suppose db table name is users and authorities. dataSource is configured in application.yml.
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from users WHERE username=?")
.authoritiesByUsernameQuery("select username,authority from authorities where username=?")
.passwordEncoder(new BCryptPasswordEncoder());
}
}

Spring security authentication failing with BCryptPasswordEncoder

I'm new to spring and I'm trying to implement spring security in a project. I was able to create user with hash password using Bcrypt, but whenever I tried to login using the same password it fails. I've also tried checking other SO answers (like Spring Security BCryptPasswordEncoder Inserted But Not Match) non solved the issues faced.
Below is what have tried so far
WebSecurityConfigurerAdapter Class
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.csrf().disable();
http.authorizeRequests().antMatchers("/login", "/app-assets/**", "/assets/**").permitAll();
http.authorizeRequests().antMatchers("/add-user", "/users-list").hasRole("ADMIN");
http.authorizeRequests().antMatchers("/", "/index", "/add-content", "/mange-content").hasAnyRole("ADMIN", "USER");
http
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/index")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
//.failureUrl("/login-error.html")
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/login?logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.permitAll();
}
#Bean
public DaoAuthenticationProvider authProvider() {
System.out.println("GOT CALLED HERE.....");
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Auto-generated method stub
//auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
auth.authenticationProvider(authProvider());
}
}
UserDetailsService
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserDAO userDAO;
#Autowired
private RoleDAO roleDAO;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDAO.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User "+username+"not fount");
}
List<String> roleNames = roleDAO.getRoleNames(user.getAdminId());
System.out.println("USERNAME: "+user.getAdminId() + " "+user.getPassword());
List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
if (roleNames != null) {
for (String role : roleNames) {
GrantedAuthority authority = new SimpleGrantedAuthority(role);
grantList.add(authority);
}
}
UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantList);
return userDetails;
}
}
TEST
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTests {
#Autowired
private UserDAO userDAO;
#Autowired
private BCryptPasswordEncoder encoder;
#Test
public void contextLoads() {
String password = "eureka";
String encrytedPassword = encoder.encode(password);
User user = userDAO.findUserByEmail("xxx#gmail.com");
System.out.println(encrytedPassword);
System.out.println("Matched: " + encoder.matches("eureka", encrytedPassword));//This line returns true
assertEquals(encrytedPassword, user.getPassword());
}
}
I also tried overriding matches() but to no avail
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder() {
#Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// This throws an error
return matches(rawPassword, encodedPassword);
}
};
}
Note: Hashed password and raw password was gotten from matches() method. So there's no issues retrieving hashpassword from database.

Java Spring Multiple #Autowired MongoRepository Usages with JWT

The successfulAuthentication function in JWTAuthenticationFilter.java gives a nullpointerexception. Do you see why this would be an issue? Is it a problem with using the same bean for autowiring?
This is my current project structure:
-com
-register
-RegisterController.java
-security
-JWTAuthenticationFilter.java
-JWTAuthorizationFilter.java
-SecurityConstants.java
-WebSecurity.java
-user
-User.java
-UserRepository.java
-UserService.java
-Application.java
Application.java
#Configuration
#SpringBootApplication
public class Application {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
UserRepository.java
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String name);
User findByEmail(String Email);
User findBy_id(ObjectId id);
}
UserService.java
#Service
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if(user == null)
return null;
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority("user"));
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities);
}
public User getUserByUsername(String username) {
return this.userRepository.findByUsername(username);
}
public User getUserBy_id(ObjectId _id) {
return userRepository.findBy_id(_id);
}
public void saveUser(User newUser){
userRepository.save(newUser);
}
}
User.java
#Document
public final class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ObjectId _id;
private String email;
private String username;
private String password;
private AccountProperties accountProperties;
private Address address;
private List<Pet> pets = new ArrayList<>();
private String phoneNumber;
public User() {}
public User(#JsonProperty("email") String email, #JsonProperty("username") String username,
#JsonProperty("password") String password) {
this.email = email;
this.username = username;
this.password = password;
}
public String get_id() { return _id.toHexString();}
getters and setters() ...
}
JWTAuthenticationFilter.java
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Autowired
private UserRepository userRepo;
private AuthenticationManager authenticationManager;
JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername();
String token = JWT.create()
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
User u = uRepo.findByUsername("admin");
res.getWriter().write(
"{\"" + SecurityConstants.HEADER_STRING + "\":\"" + SecurityConstants.TOKEN_PREFIX+token + "\"," +
"\"" + "ObjectID" + "\":\"" + u.get_id() + "\"}"
);
}
}
JWTAuthorizationFilter.java
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
WebSecurity.java
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS );
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(this.userDetailsService).passwordEncoder(this.bCryptPasswordEncoder);}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
SecurityConstants.java
public class SecurityConstants {
public static final String SECRET = "SecretKeyToGenJWTs";
public static final long EXPIRATION_TIME = 864_000_000; // 10 days
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String SIGN_UP_URL = "/users/sign-up";
}
RegisterController.java
#RestController
#RequestMapping("/users")
public class RegisterController {
#Autowired
private UserService userService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#PostMapping("/sign-up")
public void signUp(#RequestBody User user) {
if (user.getPassword() == null || user.getUsername() == null)
return;
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
userService.saveUser(user);
}
}
Not sure if this is the root cause of your issue, but I have never seen #Configuration in the main application. I would try to move that to a separate config class and see if that helps
Annotate the JWTAuthenticationFilter with #Component or add #Bean in configuration file . Looks like the object is not created
The problem is that you doesn't define JWTAuthenticationFilter as Bean, so spring doesn't inject dependencies in it.
You can get beans in filter manualy. From the GenericFilterBean javadoc:
This generic filter base class has no dependency on the Spring org.springframework.context.ApplicationContext concept. Filters usually don't load their own context but rather access service beans from the Spring root application context, accessible via the filter's ServletContext (see org.springframework.web.context.support.WebApplicationContextUtils).
Or you can make it bean. But if you are using Spring Boot consider that:
The fact that all filters internal to Spring Security are unknown to the container is important, especially in a Spring Boot application, where all #Beans of type Filter are registered automatically with the container by default. So if you want to add a custom filter to the security chain, you need to either not make it a #Bean or wrap it in a FilterRegistrationBean that explicitly disables the container registration.

Spring Security using Bcrypt and hibernate returning bad credentials

As the title says, I'm using Spring Security with JWT (using hibernate and bCrypt) to sign up users and let them login. I've followed this tutorial to make this work in my project. When doing exactly the same as the tutorial (using the in-memory-database) everythings seems to work fine. But when intergrating the code in my own project the authentication keeps failing, giving a "bad credentials" exception.
Main:
#SpringBootApplication
#EnableConfigurationProperties
public class ApiApplication {
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
My websecurity config looks like this:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsService userDetailsService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JWTAuthenticationFilter(authenticationManager()))
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
The authentication filter, whenever I try to send a post with the username and password, the unsuccessfulAuthentication runs, even with correct credentials.
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getUsername(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth) throws IOException, ServletException {
System.out.println("This method never runs...");
Claims claims = Jwts.claims()
.setSubject(((org.springframework.security.core.userdetails.User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
super.unsuccessfulAuthentication(request, response, failed);
System.out.println("FAILED");
failed.printStackTrace(); // bad creds
}
}
UserDetailServiceImpl:
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository repository;
public UserDetailsServiceImpl(UserRepository applicationUserRepository) {
this.repository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = repository.findByUsername(username);
if(user == null){
System.out.println("User is null");
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), emptyList());
}
}
The user entity:
#Data
#Entity
#Table(name = "user_entity")
public class User implements Serializable {
public User() { }
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "user_id", unique = true)
private long userId;
#NotEmpty
#Column(name = "username", unique = true)
private String username;
#NotEmpty
#Column(name = "user_rol")
#JsonProperty("userRol")
private String userRol;
#NotEmpty
#Column(name = "password")
private String password;
}
In my user controller I encrypt the password like this:
#PostMapping("/sign-up")
public User signUp(#RequestBody User user) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
user.setUserRol("ADMIN");
return userService.signUpUser(user);
}
Everythings seems to be ok, Spring succesfully puts a new user in the database (with an encrypted password), whenever I do a login attempt it also succesfully gets that user, but authentication still fails (with correct credentials ofcourse). So my guess there is something wrong with the bcypt password encoder...
Another thing I wonder; where does the /login route come from? Is this a default route within Spring Security? (I've never declared it)
Thanks for the help guys!
Maybe it is due a typo in your attemptAuthentication() method.
#Override
public Authentication attemptAuthentication(HttpServletRequest req,
HttpServletResponse res) throws AuthenticationException {
try {
User creds = new ObjectMapper()
.readValue(req.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getUsername(),
new ArrayList<>())
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
In the constructor of UsernamePasswordAuthenticationToken the second argument should be creds.getPassword() and not creds.getUsername().

How to configure security access in Spring?

I'm learning Spring and now trying to configure security. I need to create secured endpoints and one not secured for registration.
But when I'm trying to access http://localhost:8080/register I'm getting error "An Authentication object was not found in the SecurityContext" and "AuthenticationCredentialsNotFoundException".
I have repeated example in docs but still get this error.
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/register").permitAll();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
OAuth2Config.java
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Value("3600")
private int expiration;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.authenticationManager(authenticationManager);
configurer.userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("app").secret("secret").accessTokenValiditySeconds(expiration)
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token").resourceIds("resource");
}
}
RegisterController.java
#RestController
public class RegisterController {
#Autowired
UserDao userDao;
#Autowired
CityDao cityDao;
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> addUser(#RequestParam(value = "email") String email, #RequestParam(value = "firstName") String firstName,
#RequestParam(value = "lastName") String lastName, #RequestParam(value = "city") Long cityId,
#RequestParam(value = "password") String password){
User userToFind = userDao.findByEmail(email);
City city = cityDao.findById(cityId).get();
if (userToFind != null) {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
User user = new User(firstName, lastName, email, city, passwordEncoder.encode(password));
return new ResponseEntity<>(userDao.save(user), HttpStatus.CREATED);
}
else{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
}
}

Categories