I'm faced with a big issue when trying to access a my Rest API, build with Springboot from my VueJS frontend.
Backend-URL = http://localhost:8080
Frontend-URL = http://localhost:8081
In the main.js file I added the following defaults for axios:
axios.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
axios.defaults.headers.common['Access-Control-Allow-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept';
axios.defaults.withCredentials = true;
axios.defaults.crossDomain = true;
On the backend site I added the following entries:
All controllers
#CrossOrigin(origins = "http://localhost:8081")
#RestController
public class ControllerName {
...
NewFile: DevelopmentWebSecurityConfigurerAdapter (same package as main method)
package de.my.server;
import java.util.Arrays;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableWebSecurity;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import lombok.extern.slf4j.Slf4j;
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class DevelopmentWebSecurityConfigurerAdapter extends WebMvcConfigurerAdapter {
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("http://locahost:8081"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
protected void configure(HttpSecurity http) throws Exception {
//http.csrf().disable();
http
.cors()
.and()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/api").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
}
}
new Filter "CorsFilter" (simply added the java file in the same package as the main method)
package de.my.server;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
#Component
public class CorsFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request= (HttpServletRequest) servletRequest;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "false");
response.setIntHeader("Access-Control-Max-Age", 3600);
filterChain.doFilter(servletRequest, servletResponse);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
Until now nothing works...
Does anyone got a hint for my how to access my api?
Kind regards
tschaefermedia
Create a vue.config.js file in your VueJS root directory if it not exists and add this
// vue.config.js
module.exports = {
// proxy all webpack dev-server requests starting with /api
// to our Spring Boot backend (localhost:8088) using http-proxy-middleware
// see https://cli.vuejs.org/config/#devserver-proxy
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8088',
ws: true,
changeOrigin: true
},
'/fileUpload': {
target: 'http://localhost:8088',
ws: true,
changeOrigin: true
}
}
},
// Change build paths to make them Maven compatible
// see https://cli.vuejs.org/config/
outputDir: 'target/dist',
assetsDir: 'static'
}
In my case, all requests to "/api" and "/fileUpload" are forwarded to my Spring Java Backend
i hope this could help you a bit if not already solved
For more information look here
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 :(
For some reason I cannot add a filter that will take over the authorization phase of the filter chain. The defaultChain is reading out on start: "Will not secure any request", but it seems like my request are secured when I test, however It will not trigger my filter. If I could get some help on this matter it would be greatly appreciated!
WEBCONFIG
package com.legacybanking.legacyBankingAPI.security.securityConfig;
import com.legacybanking.legacyBankingAPI.security.jwt.JwtPasswordValidator;
import com.legacybanking.legacyBankingAPI.services.CustomerService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.util.List;
#Configuration
#AllArgsConstructor
#EnableWebSecurity
#Slf4j
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final CustomerService customerService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customerService).passwordEncoder(bCryptPasswordEncoder);
}
#Override
#Bean
protected UserDetailsService userDetailsService() {
return super.userDetailsService();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
log.info("ITS NOT WORKING");
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.addFilter(new JwtPasswordValidator(authenticationManager()))
.authorizeRequests()
// ANT MACHERS = WHITELISTING
.antMatchers("/api/**")
.permitAll()
.anyRequest()
.authenticated();
}
}
JWTCONFIG
package com.legacybanking.legacyBankingAPI.security.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.legacybanking.legacyBankingAPI.models.Customer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.GrantedAuthority;
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.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.stream.Collectors;
#Slf4j
public class JwtPasswordValidator extends UsernamePasswordAuthenticationFilter {
#Autowired
private final AuthenticationManager authenticationManager;
public JwtPasswordValidator(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
System.out.println("request = " + request.getParameter("email"));
System.out.println("request = " + request.getParameter("password"));
String email = request.getParameter("email");
String password = request.getParameter("password");
log.info("Test Loging -> {}, {}",email,password);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(email,password);
return authenticationManager.authenticate(authenticationToken);
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
System.out.println(authResult);
User user = (User) authResult.getPrincipal();
String key = "secretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecretsecret";
Algorithm algorithm = Algorithm.HMAC256(key.getBytes());
String access_token = JWT.create()
.withSubject(user.getUsername())
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
.withIssuer(request.getRequestURI())
.withClaim("roles", user.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()))
.sign(algorithm);
String refresh_token = JWT.create()
.withSubject(user.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + 60 * 60 * 1000))
.withIssuer(request.getRequestURI())
.sign(algorithm);
System.out.println(access_token);
log.info("This is the access token: {}", access_token);
response.addHeader("Authorization", "Bearer " + access_token);
response.setHeader("access_token", "token" + access_token);
}
}
You have to register the JwtPasswordValidator class inside the AppSecurityConfig cofig() method as follows:
http.addFilter(JwtPasswordValidator);
also you may need to create a CustomAuthorizationFilter which extends OncePerRequestFilter class and register it inside the config method as follows:
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
if need more help do not hesitate to ask.
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 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>
I am stuck configuring Spring Error mapping with Spring security in Spring Boot 1.3.5 app.
I have follow security config class:
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.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password("123").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/admin").hasRole("ADMIN").and()
.csrf().disable();
}
}
And ExceptionController :
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
#Controller
public class ExceptionController implements ErrorController {
private static final String ERROR_PATH = "/error";
#RequestMapping(value = ERROR_PATH)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String error(HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return "redirect:/";
}
#Override
public String getErrorPath() {
return ERROR_PATH;
}
}
/admin secure page works fine without ExceptionController . But when I adding it, login form not appearing, I have tried a lot of things, such as ControllerAdvice, but nothing seems work for me. What's proper solution here ?