Spring Boot Rest API #CrossOrigin Not working - rest

I know this question asked already but I have the same issue and did not find any solution
Spring Boot, I have Rest API and added a cross-origin annotation
#CrossOrigin(origins = "*", allowedHeaders = "*")
But it still showing me an error
Access to XMLHttpRequest at 'http://localhost:8080/API/findUser' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
ERROR HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false}
I also tried
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
.allowedHeaders("*");
}
};
}
But not working

Just in case if somebody found this question after some Googleing, this could potentially solve the issue.
If you are using Spring security. You should enable cors() there as well. Set your WebSecurityConfigurerAdapter like this:
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.cors() //<-- Enables CORS
.and()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest()
.hasRole("admin")
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new CustomJwtAuthenticationConverter());
}
}

Related

Disable Transfer-Encoding chunked in wiremock standalone server

I am running wiremock standalone using wiremockserver.
The application does not work in PCF because of the header Transfer-Encoding: chunked.
(If this header is present then in PCF I get the error 502 Bad Gateway: Registered endpoint failed to handle the request. and in headers x-cf-routererror endpoint_failure endpoint_failure (net/http: http/1.x transport connection broken: too many transfer encodings: ["chunked" "chunked"]).)
I don't want this header so I tried to disable the chunked encoding in wiremock:
#SpringBootApplication
public class MockApplication implements CommandLineRunner {
private final int serverPort;
private final ResourceLoader resourceLoader;
public MockApplication(#Value("${server.port}") final int serverPort, final ResourceLoader resourceLoader) {
this.serverPort = serverPort;
this.resourceLoader = resourceLoader;
}
public static void main(final String[] args) {
SpringApplication.run(MockApplication.class, args); //start springboot application
}
#Override
public void run(final String... args) {
final WireMockServer wireMockServer = new WireMockServer(options().port(serverPort+2)
.useChunkedTransferEncoding(Options.ChunkedEncodingPolicy.NEVER)
.extensions(new ResponseTemplateTransformer(false)));
wireMockServer.start(); //start wiremock server
}
Spring boot app posts data to wiremock app using restTemplate. sample request:
{
"request": {
"url": "/some-wiremock-endpoint",
"method": "POST",
"bodyPatterns": [
{
"contains": "12345"
}
]
},
"response": {
"status": 200,
"headers": null,
"body": "{\"books\":[{\"book_number\":\"12345\",\"book_code\":\"FICTION\"}]}"
}
}
When the customer uses http://localhost:8080/some-wiremock-endpoint then the springboot app connects to wiremock url http://localhost:8082/some-wiremock-endpoint to retrieve the stub data from wiremock server:
ResponseEntity<String> exchange = null;
try {
exchange = restTemplate.exchange(wiremockUrlAndUri, HttpMethod.POST, request, String.class);
//http://localhost:8082/some-wiremock-endpoint is the wiremockUrlAndUri value in this case.
When I see exchange.getHeaders() then I still see Transfer-Encoding: chunked header.
How do I disable this header in Springboot application?

How to configure spring + keycloak + jwt?

I want to permit all post request without jwt.
I have the follow configuration:
#Slf4j
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers(HttpMethod.POST).permitAll();
http.authorizeRequests().antMatchers(HttpMethod.GET).permitAll();
http.oauth2ResourceServer().jwt().and().bearerTokenResolver(this::tokenExtractor);
}
private String tokenExtractor(HttpServletRequest request) {
Cookie cookie = WebUtils.getCookie(request, "access_token");
if (cookie != null) {
log.info("Cookie found");
return cookie.getValue();
}
log.info("Cookie not found");
return null;
}
}
my yaml has:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://kc-server/realms/master
keycloak:
realm: master
resource: auth
auth-server-url: http://url
credentials:
secret: ${KEYCLOAK_SECRET}
when I try to use any POST request I always get 401 error (Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Jwt expired at 2022-03-03T15:39:50Z", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"), but expect 2xx.
What is wrong with my configuration ?

How do I extract information from an incoming JWT that was generated by an external service?

How do I extract information from an incoming JWT that was generated by an external service? (Okta)
I need to perform a database lookup of user information based on one of the fields in the JWT. (I also want method-level security based on the scope of the JWT.)
The secret seems to be in using an AccessTokenConverter to extractAuthentication() and then use that to lookup UserDetails. I am stuck because every example I can find includes setting up an Authorization Server, which I don't have, and I can't tell if the JwtAccessTokenConverter will work on the Resource Server.
My resource server runs and handles requests, but my custom JwtAccessTokenConverter is never getting called during incoming requests;
All of my requests are coming in with a principal of anonymousUser.
I am using Spring 5.1.1.
My Resource Server Configuration
#Configuration
#EnableResourceServer
public class OauthResourceConfig extends ResourceServerConfigurerAdapter {
#Value("${oauth2.audience}")
String audience;
#Value("${oauth2.baseUrl}/v1/keys")
String jwksUrl;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.authorizeRequests()
.anyRequest().authenticated()
.antMatchers("/api/**").permitAll();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenServices(tokenServices())
.resourceId(audience);
}
#Primary
#Bean
public DefaultTokenServices tokenServices() throws Exception {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
return tokenServices;
}
#Bean
public TokenStore tokenStore() {
return new JwkTokenStore(jwksUrl, accessTokenConverter());
}
#Bean
public AccessTokenConverter accessTokenConverter() {
return new CustomJwtAccessTokenConverter();
}
}
My Custom Access Token Converter
public class CustomJwtAccessTokenConverter extends JwtAccessTokenConverter {
#Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication authentication = super.extractAuthentication(map);
Authentication userAuthentication = authentication.getUserAuthentication();
if (userAuthentication != null) {
LinkedHashMap userDetails = (LinkedHashMap) map.get("userDetails");
if (userDetails != null) {
... Do the database lookup here ...
Collection<? extends GrantedAuthority> authorities = userAuthentication.getAuthorities();
userAuthentication = new UsernamePasswordAuthenticationToken(extendedPrincipal,
userAuthentication.getCredentials(), authorities);
}
}
return new OAuth2Authentication(authentication.getOAuth2Request(), userAuthentication);
}
}
And my Resource
#GET
#PreAuthorize("#oauth2.hasScope('openid')")
public Response getRecallsByVin(#QueryParam("vin") String vin,
#QueryParam("page") Integer pageNumber,
#QueryParam("pageSize") Integer pageSize) {
List<VehicleNhtsaCampaign> nhtsaCampaignList;
List<OpenRecallsDto> nhtsaCampaignDtoList;
SecurityContext securityContext = SecurityContextHolder.getContext();
Object principal = securityContext.getAuthentication().getPrincipal();
... More irrelevant code follows ...
First of all, the #PreAuthorize annotation isn't doing anything. If I change it to #PreAuthorize("#oauth2.hasScope('FooBar')") it still lets the request in.
Secondly, I need to grab other information off the JWT so I can do a user lookup in my database. I thought that by adding the accessTokenConverter() in the resource server config, the JWT would be parsed and placed into the securityContext.getAuthentication() response. Instead all I'm getting is "anonymousUser".
UPDATE: I later found out the data I need is coming in a custom header, so I don't need to extract anything from the JWT. I was never able to validate any of the suggested answers.
Are you using Spring Boot?
The Spring Security 5.1 has support for JWT access tokens. For example, you could just supply a new JwtDecoder:
https://github.com/okta/okta-spring-boot/blob/spring-boot-2.1/oauth2/src/main/java/com/okta/spring/boot/oauth/OktaOAuth2ResourceServerAutoConfig.java#L62-L84
You can create a filter that validates and sets token to SecurityContextHolder. This is what I have done in my project using jsonwebtoken dependency:
public class JWTFilter extends GenericFilterBean {
private String secretKey = 'yoursecret';
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwt = resolveToken(httpServletRequest);
if (validateToken(jwt)) {
Authentication authentication = getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String resolveToken(HttpServletRequest request){
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
} catch (MalformedJwtException e) {
} catch (ExpiredJwtException e) {
} catch (UnsupportedJwtException e) {
} catch (IllegalArgumentException e) {
}
return false;
}
}
You can then access your token from SecurityContextHolder.
For cleaner way to access token fields, I have created POJO models of my token from http://www.jsonschema2pojo.org/

Spring Boot WebSecurityConfigurerAdapter Login Redirect

I am using the following WebSecurityConfigurerAdapter code in a Spring Boot application:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER").anyRequest().permitAll()
.antMatchers("/admin/**").hasRole("EADM").anyRequest().permitAll()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("personContactEmail").passwordParameter("personRegPwd")
.successHandler(secureAuthenticationSuccessHandler)
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler());
}
When I try to access the /user page without authenticating, the app redirects me to the login page, but when I try to access the /admin page without authenticating, the app takes me to the admin home page?
I have setup a custom AuthenticationSuccessHandler. When I debug and call the /user page, it goes in here, but with /admin it does not even reach this method. I am not sure what I am missing here?
protected String determineTargetUrl(Authentication authentication) {
boolean isUser = false;
boolean isAdmin = false;
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("ROLE_USER")) {
isUser = true;
break;
} else if (grantedAuthority.getAuthority().equals("ROLE_EADM")) {
isAdmin = true;
break;
}
}
if (isUser)
{
return "/user";
}
else if (isAdmin)
{
return "/admin";
}
else
{
throw new IllegalStateException();
}
}
Any Help Please?
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER").anyRequest().permitAll()
.antMatchers("/admin/**").hasRole("EADM").anyRequest().permitAll()
antMatchers() and anyRequest() both create RequestMatchers (AntPathRequestMatcher and AnyRequestMatcher respectively) and return a RequestMatcherConfigurer. The RequestMatchers in a given RequestMatcherConfigurer are then applied sequentially when a request is processed. The first valid match is used to determine whether or not the user will be able to access the page. For this reason, your matchers should be ordered from most specific to least specific
.antMatchers("/user/**").hasRole("USER") is processed first, so /user/** requires ROLE_USER. .anyRequest().permitAll() is second, so when a request to /admin/** is processed, this request will match permitAll() and the user will be granted access to the page and .antMatchers("/admin/**").hasRole("EADM") will never be processed.
This should be resolved by removing the first .anyRequest().permitAll(). like this:
...
http
.authorizeRequests()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("EADM")
.anyRequest().permitAll()
.and()
...

Angular 2 Spring Security CSRF Token

Hi Everyone I'm having trouble setting up a security solution for my app!!
So I have a REST API Backend which runs at http://localhost:51030 and developed with Spring Framework, and for the front side I have an Angular 2 application (the latest version A.K.A. Angular 4) which runs at http://localhost:4200.
I have set the CORS configuration in the backend as seen below:
public class CORSFilter implements Filter
{
// The list of domains allowed to access the server
private final List<String> allowedOrigins = Arrays.asList("http://localhost:4200", "http://127.0.0.1:4200");
public void destroy()
{
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException
{
// Lets make sure that we are working with HTTP (that is, against HttpServletRequest and HttpServletResponse objects)
if (req instanceof HttpServletRequest && res instanceof HttpServletResponse)
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Access-Control-Allow-Origin
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", allowedOrigins.contains(origin) ? origin : "");
response.setHeader("Vary", "Origin");
// Access-Control-Max-Age
response.setHeader("Access-Control-Max-Age", "3600");
// Access-Control-Allow-Credentials
response.setHeader("Access-Control-Allow-Credentials", "true");
// Access-Control-Allow-Methods
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
// Access-Control-Allow-Headers
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + CSRF.REQUEST_HEADER_NAME); // + CSRF.REQUEST_HEADER_NAME
}
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig)
{
}
}
Using this configuration only works fine, I can execute requests from the angular app to the spring back and get response and do anything.
But when I try to set up CSRF security solution nothing works.
This is the CSRF and Security configuration setted up in the backend:
public class CSRF
{
/**
* The name of the cookie with the CSRF token sent by the server as a response.
*/
public static final String RESPONSE_COOKIE_NAME = "XSRF-TOKEN"; //CSRF-TOKEN
/**
* The name of the header carrying the CSRF token, expected in CSRF-protected requests to the server.
*/
public static final String REQUEST_HEADER_NAME = "X-XSRF-TOKEN"; //X-CSRF-TOKEN
// In Angular the CookieXSRFStrategy looks for a cookie called XSRF-TOKEN
// and sets a header named X-XSRF-TOKEN with the value of that cookie.
// The server must do its part by setting the initial XSRF-TOKEN cookie
// and confirming that each subsequent state-modifying request includes
// a matching XSRF-TOKEN cookie and X-XSRF-TOKEN header.
}
public class CSRFTokenResponseCookieBindingFilter extends OncePerRequestFilter
{
protected static final String REQUEST_ATTRIBUTE_NAME = "_csrf";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
CsrfToken token = (CsrfToken) request.getAttribute(REQUEST_ATTRIBUTE_NAME);
Cookie cookie = new Cookie(CSRF.RESPONSE_COOKIE_NAME, token.getToken());
cookie.setPath("/");
response.addCookie(cookie);
filterChain.doFilter(request, response);
}
}
#Configuration
public class Conf extends WebMvcConfigurerAdapter
{
#Bean
public CORSFilter corsFilter()
{
return new CORSFilter();
}
#Override
public void addViewControllers(ViewControllerRegistry registry)
{
registry.addViewController("/login");
registry.addViewController("/logout");
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private RESTLogoutSuccessHandler logoutSuccessHandler;
#Resource
private CORSFilter corsFilter;
#Autowired
private DataSource dataSource;
#Autowired
public void globalConfig(AuthenticationManagerBuilder auth) throws Exception
{
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery("select login as principal, password as credentials, true from user where login = ?")
.authoritiesByUsernameQuery("select login as principal, profile as role from user where login = ?")
.rolePrefix("ROLE_");
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
//csrf is disabled for the moment
//http.csrf().disable();
//authorized requests
http.authorizeRequests()
.antMatchers("/api/users/**").permitAll()
.antMatchers(HttpMethod.OPTIONS , "/*/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated();
//handling authentication exceptions
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);
//login configuration
http.formLogin()
.loginProcessingUrl("/login")
.successHandler(authenticationSuccessHandler);
http.formLogin()
.failureHandler(authenticationFailureHandler);
//logout configuration
http.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler);
//CORS configuration
http.addFilterBefore(corsFilter, ChannelProcessingFilter.class);
//CSRF configuration
http.csrf().requireCsrfProtectionMatcher(
new AndRequestMatcher(
// Apply CSRF protection to all paths that do NOT match the ones below
// We disable CSRF at login/logout, but only for OPTIONS methods to enable the browser preflight
new NegatedRequestMatcher(new AntPathRequestMatcher("/login*/**", HttpMethod.OPTIONS.toString())),
new NegatedRequestMatcher(new AntPathRequestMatcher("/logout*/**", HttpMethod.OPTIONS.toString())),
new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.GET.toString())),
new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.HEAD.toString())),
new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.OPTIONS.toString())),
new NegatedRequestMatcher(new AntPathRequestMatcher("/api*/**", HttpMethod.TRACE.toString()))
)
);
// CSRF tokens handling
http.addFilterAfter(new CSRFTokenResponseCookieBindingFilter(), CsrfFilter.class);
}
}
The problem is in the front side and the angular 4 configuration, the CSRF documentation is so poor and there is no full example of CSRF implementation in the Internet.
So below is my login service:
#Injectable()
export class LoginService {
private loginUrl = 'http://localhost:51030/login';
constructor(private http: Http) {}
preFlight() {
return this.http.options(this.loginUrl);
}
login(username: string , password: string) {
let headers = new Headers();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
let options = new RequestOptions({headers: headers});
let body = "username="+username+"&password="+password;
return this.http.post(this.loginUrl , body , options);
}
}
And in the login component I execute the option request in the ngOnInit life cycle hook:
#Component({
templateUrl: './login-layout.component.html'
})
export class LoginLayoutComponent implements OnInit {
credentials = {username: '' , password: ''};
constructor(private loginService: LoginService){}
ngOnInit() {
this.loginService.preFlight()
.subscribe();
}
login() {
this.loginService.login(this.credentials.username , this.credentials.password)
.subscribe(
response=>{
console.log(response) ;
},error=>{
console.log(error);
}
);
}
}
The preflight goes well and I get the 200 OK status on the options request plus a temporary JSEEIONID and the XSRF-TOKEN Cookie.
So in my app module I added this as said in the angular docs:
{
provide: XSRFStrategy,
useValue: new CookieXSRFStrategy('XSRF-TOKEN', 'X-XSRF-TOKEN')
},
BUT, when I try to execute a POST request with the credentials or any request to the back I got 403 Forbidden: "Could not verify the provided CSRF token because your session was not found."
So Please how can I solve this, can any one point me to right direction cause I have no clue on how to make this work!!
And Thanks!!!
To solve the csrf problem between spring security and angular, you have to do that.
In SecurityConfiguration (WebSecurityConfig),replace http.csrf().disable(); by
http.csrf()
.ignoringAntMatchers ("/login","/logout")
.csrfTokenRepository (this.getCsrfTokenRepository());
}
private CsrfTokenRepository getCsrfTokenRepository() {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
tokenRepository.setCookiePath("/");
return tokenRepository;
{
the default angular csrf interceptor does not always work.So you have to implement your own interceptor.
import {Injectable, Inject} from '#angular/core';
import {HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler,
HttpEvent} from '#angular/common/http';
import {Observable} from "rxjs";
#Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {
constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let requestMethod: string = req.method;
requestMethod = requestMethod.toLowerCase();
if (requestMethod && (requestMethod === 'post' || requestMethod === 'delete' || requestMethod === 'put')) {
const headerName = 'X-XSRF-TOKEN';
let token = this.tokenExtractor.getToken() as string;
if (token !== null && !req.headers.has(headerName)) {
req = req.clone({headers: req.headers.set(headerName, token)});
}
}
return next.handle(req);
}
}
And finally add it in your providers (app.module.ts)
providers: [{ provide: HTTP_INTERCEPTORS, useClass: HttpXsrfInterceptor, multi: true }]
Think about putting in your imports.
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-CSRF-TOKEN'
}),
I am surprised that you are doing so much work for CSRF and CORS as Spring Security and Angular have support built in. Spring Security has CSRF enabled by default.
The spring security manual has good documentation about configuring csrf:
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf
And googling for "Angular 2 Spring Security csrf" gives several examples (and also how I found your post). Here is one:
https://medium.com/spektrakel-blog/angular2-and-spring-a-friend-in-security-need-is-a-friend-against-csrf-indeed-9f83eaa9ca2e