This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 5 years ago.
In my application i am trying to give "/user/** " permission to USER and "/admin/**" permission to admin user but I am getting 403 error.
I am using spring boot 1.5.3
Security configuration class :
package com.alokpanda.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#Order(1)
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login", "/logout").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
.failureUrl("/")
.and()
.logout()
//.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutUrl("/logout")
.logoutSuccessUrl("/")
.and()
.csrf()
.disable();
}
}
Authencation provider class :
package com.alokpanda.security.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import com.alokpanda.security.service.CustomUserDetailsService;
#Service
public class AuthenticationProviderImpl extends AbstractUserDetailsAuthenticationProvider {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken token)
throws AuthenticationException {
System.out.println(userDetails.getUsername());
System.out.println(userDetails.getPassword());
System.out.println(token.getCredentials());
System.out.println(token.getCredentials().equals(userDetails.getPassword()));
System.out.println(userDetails.getAuthorities());
if(userDetails.getUsername() == null || token.getCredentials() == null) {
throw new BadCredentialsException("Credential may not be null.");
}
if(!token.getCredentials().equals(userDetails.getPassword())) {
System.out.println("Err");
throw new BadCredentialsException("Invalid Credentials.");
}
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken token)
throws AuthenticationException {
UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
return userDetails;
}
}
UserDetailsService class :
package com.alokpanda.security.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.alokpanda.model.User;
import com.alokpanda.model.UserRole;
import com.alokpanda.repository.UserRepository;
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
for(UserRole userRole : user.getUserRole()) {
grantedAuthorities.add(new SimpleGrantedAuthority(userRole.getRole()));
}
UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities);
return userDetails;
}
}
By default, spring security add ROLE_ prefix to your roles.
Save the roles in your DB as ROLE_USER and ROLE_ADMIN.
Related
I'm trying to make a login/registration in Spring Boot but I'm facing some problems.
Registration and login works just fine, but I want to check and verify the token at every request sent.
This is the JwtUsernameAndPasswordAuthenticationFilter:
package com.example.springsecurityproject.jwt;
import com.example.springsecurityproject.entity.UserEntity;
import com.example.springsecurityproject.repository.UserRepository;
import com.example.springsecurityproject.service.TokenService;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.apache.catalina.User;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.crypto.SecretKey;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Date;
import java.time.LocalDate;
#AllArgsConstructor
public class JwtUsernameAndPasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private TokenService tokenService;
private AuthenticationManager authenticationManager;
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
UsernameAndPasswordAuthenticationRequest authenticationRequest =
new ObjectMapper().readValue(request.getInputStream(), UsernameAndPasswordAuthenticationRequest.class);
Authentication authentication = new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
);
Authentication authenticate = authenticationManager.authenticate(authentication);
return authenticate;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
String secretKey = Encoders.BASE64.encode(key.getEncoded());
tokenService.addToken(secretKey, (UserEntity) authResult.getPrincipal());
String JWTtoken = Jwts.builder()
.setSubject(authResult.getName())
.claim("authorities", authResult.getAuthorities())
.setIssuedAt(new java.util.Date())
.setExpiration(Date.valueOf(LocalDate.now().plusWeeks(2)))
.signWith(Keys.hmacShaKeyFor(secretKey.getBytes()))
.compact();
response.addHeader("Authorization", "Bearer" + JWTtoken);
}
}
In this when the Key is created I'm saving it into a database where there is the encrypted key and the UserEntity (as foreign key).
In the JwtVerifierToken I'm usign a function getKeyByUser that just take the key when I pass the UserEntity, but the method request.getUserPrincipal() returns null, that means I can't have the key and also cannot verify.
package com.example.springsecurityproject.jwt;
import com.example.springsecurityproject.entity.TokenEntity;
import com.example.springsecurityproject.entity.UserEntity;
import com.example.springsecurityproject.repository.TokenRepository;
import com.example.springsecurityproject.service.UserService;
import com.google.common.base.Strings;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Key;
public class JwtTokenVerifier extends OncePerRequestFilter {
private final TokenRepository tokenRepository;
private UserService userService;
public JwtTokenVerifier(TokenRepository tokenRepository) {
this.tokenRepository = tokenRepository;
}
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("Authorization");
if (Strings.isNullOrEmpty(authorizationHeader) || !authorizationHeader.startsWith("Bearer")){
filterChain.doFilter(request,response);
return;
}
try {
TokenEntity key = tokenRepository.getKeyByUser((UserEntity) request.getUserPrincipal());
String token = authorizationHeader.replace("Bearer", "");
Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey((Key) key).build().parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = body.getSubject();
}
catch (JwtException e){
throw new IllegalStateException("Token non verificato");
}
}
}
This is the ApplicationSecurityConfig
package com.example.springsecurityproject.security;
import com.example.springsecurityproject.jwt.JwtTokenVerifier;
import com.example.springsecurityproject.jwt.JwtUsernameAndPasswordAuthenticationFilter;
import com.example.springsecurityproject.repository.TokenRepository;
import com.example.springsecurityproject.repository.UserRepository;
import com.example.springsecurityproject.service.TokenService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import static org.springframework.security.config.http.SessionCreationPolicy.*;
#Slf4j
#Configuration
#EnableWebSecurity
#AllArgsConstructor
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
private final TokenService tokenService;
private final TokenRepository tokenRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(STATELESS)
.and()
//.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) (per averlo abilitato)
.addFilter(new JwtUsernameAndPasswordAuthenticationFilter(tokenService,authenticationManager()))
.addFilterAfter(new JwtTokenVerifier(tokenRepository),JwtUsernameAndPasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/register").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
/*
Form Based Login
.formLogin()
.loginPage("/login")
.permitAll()
.defaultSuccessUrl("/courses", true)
.passwordParameter("password")
.usernameParameter("username")
.and()
.rememberMe()
.tokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(31))//default 2 weeks
.key("verysecurekey")
.rememberMeParameter("remember-me")
.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.clearAuthentication(true)
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID", "remember-me")
.logoutSuccessUrl("/login");
*/
}
/*
#Override
#Bean
protected UserDetailsService userDetailsService() {
UserDetails vitoUser = User.builder()
.username("vito")
.password(passwordEncoder.encode("password"))
//.roles(STUDENT.name())
.authorities(STUDENT.getGrantedAuthorities())
.build();
UserDetails ddbdevUser = User.builder()
.username("ddbdev")
.password(passwordEncoder.encode("password"))
//.roles(ADMIN.name())
.authorities(ADMIN.getGrantedAuthorities())
.build();
UserDetails marioUser = User.builder()
.username("benmar")
.password(passwordEncoder.encode("password"))
//.roles(MODERATOR.name())
.authorities(MODERATOR.getGrantedAuthorities())
.build();
return new InMemoryUserDetailsManager(
ddbdevUser,
vitoUser,
marioUser
);
}
*/
}
Can someone help? I'm stuck :(
I've been following issues such as this one in order to figure out how to implement Authentication without WebSecurityConfigurerAdapter, but my code simply fails to work.
This is my SecurityConfig class:
package com.authentication.take.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import com.authentication.take.services.CustomUserDetailsService;
#Configuration
#EnableWebSecurity
public class SecurityConfig {
private final CustomUserDetailsService customUserDetailsService;
private final AuthenticationConfiguration configuration;
public SecurityConfig(CustomUserDetailsService customUserDetailsService,
AuthenticationConfiguration configuration) {
super();
this.customUserDetailsService = customUserDetailsService;
this.configuration = configuration;
}
#Bean
public PasswordEncoder getPasswordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Bean
protected SecurityFilterChain filterChain(HttpSecurity http)
throws Exception {
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/yolo/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login").permitAll()
.and()
.logout().permitAll();
return http.build();
}
#Bean
AuthenticationManager authenticationManager() throws Exception {
return configuration.getAuthenticationManager();
}
void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(customUserDetailsService).passwordEncoder(getPasswordEncoder());
}
}
The problem I am getting is that the /login route is being overriden somehow, and cannot be found. Therefore, when I post data to /login, it isn't interpreted because there is no form in that location.
just add loginProcessingUrl()
....
http
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/yolo/**").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginPage("/login").permitAll().loginProcessingUrl("/login")//or any another url ,this url accept just post request
.and()
.logout().permitAll();
I'm trying to configure Spring Security authorization but I'm getting 403 (forbidden) for each Postman request.
I checked the other questions but nothing works. Can anyone who got this problem and resolved it share what I need to do to fix it?
I want to add an authorization to make /authenticate accessible for all users, /registeradmin, /registersimpleuser and /listallusers only for the admin role.
I'm getting 403 even in /authenticate which is configured as permit all.
Spring Security config class:
package com.project.encheres.security.configuration;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import com.project.encheres.repository.UserRepository;
#SpringBootApplication(exclude = {SecurityAutoConfiguration.class })
#Configuration
#EnableWebSecurity
#Component
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserRepository userRepo;
#Autowired
UserDetailsService userDetailsService;
public SecurityConfiguration(UserRepository userRepo) {
this.userRepo = userRepo;
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "localhost:8070/user/registersimpleuser").permitAll();
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "localhost:8070/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "localhost:8070/user/listallusers").hasAuthority("ADMIN");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
public BCryptPasswordEncoder gePasswordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "localhost:8070/user/registersimpleuser").permitAll();
http
.httpBasic()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "localhost:8070/authenticate").permitAll()
.antMatchers(HttpMethod.GET, "localhost:8070/user/listallusers").hasAuthority("ADMIN");
http
.csrf().disable();
}
Just added http.csrf().disable(); in the end of the method.
this will describe why your code was not working earlier
I'm struggling with log into my application with postman. I'm using spring security with simple configuration:
package main.configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
#EnableWebSecurity()
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
public WebSecurityConfiguration(#Qualifier("userDetailsServiceImpl") UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/createUser").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
#Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers(HttpMethod.POST, "/createUser")
.antMatchers(HttpMethod.POST, "/login");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
I'm sending POST request on /loginso that I could log in, but all the time I get:
photo from postman
Could you tell me what I'm doing wrong here? How am I supposed to log in and oparte on postman normally? What is interesting if I try to get page within my browser, I'm asked for credentials and then I'm succesfully logged in.
I want to access my other pages which are protected and test them using POSTMAN.
Here is the answer, lack of httpBasic()
Conf class:
package main.configuration;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
#Configuration
#EnableWebSecurity()
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
public WebSecurityConfiguration(#Qualifier("userDetailsServiceImpl") UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/createUser").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll().and().httpBasic();
http.csrf().disable();
}
#Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers(HttpMethod.POST, "/createUser");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
In that case can you please change below as of now and take it from there ?
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1").password(passwordEncoder().encode("user1Pass")).roles("POSTMAN");
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.defaultSuccessUrl("/success.html", true) // example page where request need to be redirected when login is sucessful.
.and()
.logout()
.deleteCookies("JSESSIONID");
}
Try above username and password using in memory implementation first and then configure databaseservice later.
Create success.html and you should see the same after login attempt
If you want to test internal URL's through POSTMAN without authentication/ authorization then comment .anyRequest().authenticated() during testing.
I am trying to use Spring Security in my REST API using JWT tokens but everytime I am trying to make a login using the endpoint: /login of my Api, I am getting a 403 Forbidden and I have no idea why, with a completely empty body message.
This is my WebSecurity extenson configuration:
package com.debtServer.authentication;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.context.annotation.Bean;
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurity(UserDetailsServiceImpl 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, SecurityConstants.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(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
And the following two classes are the two filteres configured:
package com.debtServer.authentication;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
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(SecurityConstants.HEADER_STRING);
if (header == null || !header.startsWith(SecurityConstants.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(SecurityConstants.HEADER_STRING);
if (token != null) {
// parse the token.
String user = Jwts.parser()
.setSigningKey(SecurityConstants.SECRET.getBytes())
.parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
.getBody()
.getSubject();
if (user != null) {
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
}
Second filter:
package com.debtServer.authentication;
import com.debtServer.model.LoginBindingModel;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import static com.debtServer.authentication.SecurityConstants.*;
import com.debtServer.persistence.entity.*;
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 {
LoginBindingModel loginCredentials = new ObjectMapper()
.readValue(req.getInputStream(), LoginBindingModel.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginCredentials.getUsername(),
loginCredentials.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 token = Jwts.builder()
.setSubject(((User) auth.getPrincipal()).getUsername())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
.compact();
res.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
And finally my implementation of the user details:
package com.debtServer.authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.debtServer.persistence.repository.UserRepository;
import com.debtServer.persistence.entity.*;
import java.util.List;
import static java.util.Collections.emptyList;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
private UserRepository applicationUserRepository;
public UserDetailsServiceImpl(UserRepository applicationUserRepository) {
this.applicationUserRepository = applicationUserRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserEntity applicationUser = applicationUserRepository.findByNEmail(username);
if (applicationUser == null) {
throw new UsernameNotFoundException(username);
}
return new User(applicationUser.getUsername(), applicationUser.getPassword(), emptyList());
}
}
I am using Spring Security.
Update: I have found an error in my console:
java.lang.ClassNotFoundException: javax.xml.bind.DatatypeConverter
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:185) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:496) ~[na:na]
at io.jsonwebtoken.impl.Base64Codec.encode(Base64Codec.java:21) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.Base64UrlCodec.encode(Base64UrlCodec.java:22) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtBuilder.base64UrlEncode(DefaultJwtBuilder.java:349) ~[jjwt-0.9.0.jar:0.9.0]
at io.jsonwebtoken.impl.DefaultJwtBuilder.compact(DefaultJwtBuilder.java:295) ~[jjwt-0.9.0.jar:0.9.0]
at com.debtServer.authentication.JWTAuthenticationFilter.successfulAuthentication(JWTAuthenticationFilter.java:58) ~[classes/:na]
Line 58 is this one: .compact();
I have just find out the problem. The problem was I was using Java 9, which seems not to be compatible with some of the extensions I was using. I changed to 8 and it is now ok.
Maybe you missed csrf token. Spring security default csrf configuration value is enabled and without csrf token spring security returns 403 status.
Here's what is csrf and how to add csrf token. :
https://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html#csrf-using
Add the dependency inside pom.xml below to solve the problem.
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>