How can the web page with Spring Boot load images from imgur? - spring-boot

My web page would ajax(GET) and get the blog data from RESTFul backend.
After I got the data, my js program would update the page.
The data I got contained HTML tag, like:
<p>Hello</p>
<img src="https://i.imgur.com/xxxxxx.jpg">
But after updated my web page, the 403 error was appeared because the images can not be obtain from imgur.
My web page was updated successfully except the images didn't load.
This is my WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailService customUserDetailService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/", "/*.*", "/getPost/**", "/css/**", "/js/**", "/tinymce/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
//.loginPage("/Login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.customUserDetailService)
.passwordEncoder(this.getPasswordEncoder());
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*", "https://i.imgur.com"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers","Access-Control-Allow-Origin","Access-Control-Request-Method", "Access-Control-Request-Headers","Origin","Cache-Control", "Content-Type", "Authorization"));
configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
How can I fix this problem ?
Thanks!

I thought i solved it...
If I used http://127.0.0.1, the images from imgur could not be obtain.
But I was successful if I used real IP to connect my website.

Related

403 error when requesting POST or PUT during Spring RestAPI development

I am trying to implement and use Rest API in a project to which Spring 5 + Spring Security 5 is applied.
When testing with Postman after running Tomcat locally
url: http://localhost:8080/api~
It has been confirmed that requests such as get, post, and put work normally.
I uploaded this project to https server and while testing the api on the server in local Postman,
A 403 forbidden error occurred in requests such as put and post, excluding get requests.
When I googled, it said that Spring Security's csrf problem + cors handling problem.
So I changed the code like that.
WebSecurityConfig
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
~
.anyRequest().permitAll()
.and()
.formLogin()
~
.and()
.logout()
~
.and()
.httpBasic().disable().cors().configurationSource(corsConfigurationSource())
.and()
.sessionManagement()
~
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", corsConfiguration);
return source;
}
WebMvcConfig
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedOrigins("*");
}
Controller
#CrossOrigin("*")
But in freflight request it returns 200 normally, but in this request I still get 403.
Which part is wrong? I would appreciate it if you let me know :)
In your configuration add add Cors filter via the HttpSecurity builder.
i.e.
Override
protected void configure(HttpSecurity http) throws Exception {
http.cors()
.........
This will enable cors preflight requests which are used by PUT and POST.
you should not need to include a CorsConfigurationSource, just make sure you have the #CrossOrigin annotation in your Controllers as well.

Providing custom OAuth2AccessTokenResponseClient to spring

I have a demo spring boot app and I would like to configure oauth login as well my own custom token response client.
This is my configuration:
#Configuration
#EnableWebSecurity
class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.and()
.oauth2Client()
.authorizationCodeGrant()
.accessTokenResponseClient(customAccessTokenResponseClient());
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> customAccessTokenResponseClient() {
DefaultAuthorizationCodeTokenResponseClient client = new DefaultAuthorizationCodeTokenResponseClient();
client.setRequestEntityConverter(new CustomOAuth2AuthorizationCodeGrantRequestEntityConverter());
return client;
}
}
The login flow is working find, but it looks like my custom token response client is not getting registered. I put a break point in there, but the flow doesn't go through it. It looks like it is still using the defaults.
Am I missing something here or is the order wrong?
Figured out the issue.
The right way to configure is the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2Login()
.tokenEndpoint()
.accessTokenResponseClient(customAccessTokenResponseClient());
}

global cors configuration does not work in spring boot but indivisual #CrossOrigins work

I have below configs:
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","HEAD","DELETE","PUT"));
configuration.setMaxAge(1l);
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
and security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests()
.antMatchers(
"/auth/**"
).permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.anyRequest().authenticated()
;
}
but this mix does not work. If I remove CorsConfigurationSource corsConfigurationSource() block and add #CrossOrigin(origins = "*", maxAge = 1) cors problems will gone!
but I want to register cors globally, what is the problem?
I use spring boot version 2, with spring security and some rest controllers.
(maxAge=1) added because of browser caching that waste a lot of my time!
(if by any chance, browser skips preflight step, why server does not check for origin in real call? that must be checked in server or in client by browser?)
Check by adding this property.
configuration.setAllowedHeaders(Arrays.asList("*"));

Spring Boot Authorization Basic Header Never Changes

I am attempting to setup a very basic spring boot authenticated application. I am setting the Authorization header in the client and sending it to the backend. I can verify that the client is sending the correct header.
The backend receives the header correctly on the first attempt to login. However if the login credentials are incorrect subsequent requests retain whatever the header for the intial request was (caching it or something).
I am using Redis to Cache the session. My config is as follows:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired AuthenticationEntryPoint authenticationEntryPoint;
#Autowired CsrfTokenRepository csrfTokenRepository;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.authorizeRequests().antMatchers("**")
.permitAll()
.anyRequest()
.authenticated()
;
}
}
AuthenticationEntryPoint
public class AuthenticationEntryPointBean {
#Bean
AuthenticationEntryPoint authenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
}
Any direction would be appreciated.
** Edit **
Adding cache settings
#Configuration
#EnableRedisHttpSession
public class HttpSessionConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(); // <2>
}
}
Also I am trying to invalidate cache but that doesn't seem to work
#CrossOrigin
#RequestMapping(value="/auth/login", method = RequestMethod.GET, produces="application/json")
public #ResponseBody String login(#RequestHeader(name = "authorization") String authorization, HttpSession session, HttpServletRequest request)
{
try
{
authorization = authorization.substring("Basic ".length());
String decoded = new String(Base64.getDecoder().decode(authorization),"UTF-8");
Gson gson = new Gson();
LoginRequest login = gson.fromJson(decoded,LoginRequest.class);
UserAuthenticationEntity entity = service.getSecurityContext(login).orElseThrow(() ->
new BadCredentialsException("Authentication Failed.")
);
session.setMaxInactiveInterval((int)TimeUnit.MINUTES.toSeconds(expiresInMinutes));
SecurityContextHolder.getContext().setAuthentication(new EntityContext(entity,expiresInMinutes));
String response = gson.toJson(BasicResponse.SUCCESS);
return response;
}
catch (Exception e)
{
session.invalidate();
e.printStackTrace();
throw new AuthenticationCredentialsNotFoundException("Authentication Error");
}
}
Adding the following to my web security config seemed to do the trick.
.requestCache()
.requestCache(new NullRequestCache())
I am not sure what side effects are of doing this. I picked it up off of a blog https://drissamri.be/blog/2015/05/21/spring-security-and-spring-session/
If there is any more insight into if this is good practice or bad practice I would appreciate any comments.
My final web security config looks like the following:
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf()
.disable()
.authorizeRequests().antMatchers("**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.sessionManagement()
.sessionFixation()
.newSession()
;

Problems using Spring login in REST with CORS

I am trying to implement a website using REST. My strategy to authenticate the users consist of sending a JWT token to the user in reply to a username/password combination sent via POST. The most relevant part of my security conf is shown below.
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/images/**", "/scripts/**", "/styles/**", "favicon.ico");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
return new RESTAuthenticationSuccessHandler(jWTTokenService());
}
#Bean
public JWTTokenService jWTTokenService() {
return new JWTTokenServiceImpl();
}
To allow the CORS access I have written the following lines in a class extending of WebMvcConfigurerAdapter
#Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("GET", "POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
registry.addMapping("/login")
.allowedOrigins("*")
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept")
.allowedMethods("POST", "OPTIONS")
.allowCredentials(true).maxAge(3600);
}
So when I make a call to /login sending the username and password it is supposed that Spring will catch the request, will process it and then will redirect to the success or failure handler.
Well, instead of that I have gotten an 403 Forbidden response during the CORS preflight. I decide to debug the program because I thought that when I wrote formLogin(), the UsernamePasswordAuthenticationFilter create a new AntPathRequestMatcher with the value ("/login", "POST").
What I found in the debug console was the following
Request 'OPTIONS /login' doesn't match 'POST /login
Of course it does not! Some hours later trying to solve the problem I discovered that everything works if I declare a empty method /login because during the preflight Spring finds the method and then send a 200OK to the client so the client then is allowed to send a POST that is captured by the UsernamePasswordAuthenticationFilter.
#Controller
public class LoginController {
#RequestMapping(value = { "/login" }, method = RequestMethod.POST)
public void dummyLogin() {
}
}
So, my question is: Should I really declare an empty method to "cheat" during the CORS preflight or it is just that I have missed something? Because it is not so elegant to declare a dummy method when you really want to delegate the job to the UsernamePasswordAuthenticationFilter...
The problem is that org.springframework.security.web.authentication.logout.LogoutFilter and org.springframework.security.web.authenticationUsernamePasswordAuthenticationFilter do not continue with the filter chain if they handled a login/logout. And since the configuration via WebSecurityConfigurerAdapter is processed later in the chain, the CorsProcessor is never applied.
I decided to keep the old solution and use a org.springframework.web.filter.CorsFilter.
It is not necessary to have empty method to make it work. The only thing you have to do is to allow OPTIONS call on the /login URL.
.antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
Ex :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.authenticationEntryPoint(new RESTAuthenticationEntryPoint()).and()
.formLogin()
.successHandler(authenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.loginProcessingUrl("/login") //Not necesary because is the default
.permitAll().and()
.authorizeRequests()
.antMatchers("/api/getStatistics").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/login").permitAll()
.anyRequest().denyAll().and()
.addFilterBefore(new JwtTokenAuthenticationFilter(jWTTokenService()), UsernamePasswordAuthenticationFilter.class);
}

Resources