Delete not working in spring - java

#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
My JS FILE:
$scope.del = function (record) {
if (confirm('Do you really want to delete?')){
$http['delete']('/camera/list/' + record.filename).then(function() {
$scope.records.splice($scope.records.indexOf(record), 1);
});
}
};
My delete controller:
#RequestMapping(value = "/list/{fn}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Record> deleteUser(#PathVariable("fn") String filename) {
System.out.println("Fetching & Deleting data " + filename);
Record user1 = rep.findByfilename(filename);
if (user1 == null) {
System.out.println("Unable to delete." + filename + " not found");
return new ResponseEntity<Record>(HttpStatus.NOT_FOUND);
}
rep.deleteByfilename(filename);
return new ResponseEntity<Record>(HttpStatus.NO_CONTENT);
}
}
My Repository:
public interface RecordRepository extends MongoRepository<Record, String> {
#Query("{ 'filename' : ?0 }")
Record findByfilename(String filename);
long deleteByfilename(String filename);
}
When I click on delete button, it shows me this error:
DELETE
XHR
http://localhost:8086/camera/list/2fb1a2e020285cd91dc68a4fa7822151 [HTTP/1.1 403 Forbidden 14ms]
Anybody know what is the error? At first my delete worked but when I used spring security my delete is not working.

You need to review your spring security config:
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
When you say anyRequest().authenticated(), it means that all requests should be authenticated.
If you want to allow camera/list to be called without authentication add it to permitAll()

Related

Spring security : managing two Authentication Provider

I need to have 2 implementations of AuthenticationProvider required for spring security.
One is WindowsAuthenticationProvider (retrieve the user logged in windows - through waffle connector)
So I configure like this :
#Configuration
#EnableWebSecurity
public class WaffleConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
//basic auth for rest endpoint callers
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/**")
.authenticated()
.and()
.authenticationProvider(inMemoryAuthenticationProvider)
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//waffle for spring mvc users (with prime faces)
.and()
.antMatcher("/secure/**")
.authorizeRequests()
.antMatchers("/secure/**").authenticated()
.and()
.authenticationProvider(windowsAuthProvider)
.httpBasic()
.authenticationEntryPoint(negotiateSecurityFilterEntryPoint)
.and()
.addFilterBefore(negotiateSecurityFilter, BasicAuthenticationFilter.class);
}
}
The problem is this line :
.authenticationProvider(inMemoryAuthenticationProvider)
I just want to do like my inmemory authentication :
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
GrantedAuthority ga = new GrantedAuthority() {
#Override
public String getAuthority() {
return "eligibilite:read";
}
};
GrantedAuthority gaw = new GrantedAuthority() {
#Override
public String getAuthority() {
return "eligibilite:write";
}
};
auth.inMemoryAuthentication()
.withUser("francois")
.password("{noop}fournel")
.authorities(Arrays.asList(ga))
.and()
.withUser("francois2")
.password("{noop}fournel2")
.authorities(Arrays.asList(gaw));
}
For windows connection through waffle I already defined the auth provider :
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(windowsAuthProvider)
}
I need to define the "In memory authentication provider", but I don't know how to define and create this object.

Unauthorized Spring Security

I am programing a Springboot api rest but i have a problem with Spring security.
When i want to Make a request to the server , it throws Unauthorized 401 but i have already configured spring security. Here is the code:
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/characters/**").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
#Override
#Bean
protected UserDetailsService userDetailsService() {
UserDetails admin= User.builder().username("admin").password(passwordEncoder().encode("123")).roles("ADMIN")
.build();
UserDetails user= User.builder().username("user").password(passwordEncoder().encode("123")).roles("USER")
.build();
return new InMemoryUserDetailsManager(admin,user);
}
}
Request method:
#PreAuthorize("hasRole('ADMIN')")
#RequestMapping(value ="/characters" ,method = RequestMethod.GET)
public List<ImagenNombreDTO> listarPersonajes(){
try {
return personajeService.listarPersonajes();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Make following changes inside antmatchers() method of SecurityConfig.java file.
Add one more entry of "/characters" endpoint like following and see whether the error still persists or not.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.GET, "/characters","/characters/**").permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}

How to bypass spring security on an authenticated endpoint for specific domain?

I am using jwt token based spring security.
I have an endpoint '/sample-endpoint' which requires authentication. However, I need to bypass security for this endpoint when the request comes from a specific domain called xyz.com.
Is it possible to do so? If so, how to do that?
Here is what I have so far.
SecurityConfig
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// cant add the end point here as it would open up for everybody.
public static final String[] unauthedUrls = { "healthcheck","some-other-endpoint"}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.disable()
.csrf()
.disable()
.cors()
.and()
.headers().frameOptions()
.disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.addFilterAfter(jwtSecurityFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers(unauthedUrls)
.permitAll()
.anyRequest()
.authenticated();
}
Here is JwtSecurityFilter implementation.
public class JwtSecurityFilter extends OncePerRequestFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtSecurityFilter.class);
private static final String JWT_PREFIX = "Bearer ";
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
setAuthenticationContext(request);
chain.doFilter(request, response);
}
private void setAuthenticationContext(HttpServletRequest request) {
try {
String token = getJwt(request);
if (StringUtils.isBlank(token)) {
throw new RuntimeException("Authorization token not provided");
}
// some logic here...
} catch (Exception ex) {
if (request != null && Arrays.stream(SecurityConfig.unauthedUrls).anyMatch(url -> request.getRequestURI().contains(url))) {
// it's a URL that isn't authenticated so an exception here is normal
// if we couldn't get a token
return;
}
LOGGER.warn("Unable to authenticate request: {} {}", ex.getMessage(), request == null ? null : request.getRequestURI());
}
}
private String getJwt(HttpServletRequest request) {
String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if (StringUtils.isBlank(authHeader) || !authHeader.startsWith(JWT_PREFIX)) {
return "";
}
return authHeader.replaceFirst(Pattern.quote(JWT_PREFIX), "");
}
}
What you want is to ignore certain URLs for this override the configure method that takes WebSecurity object and ignores the pattern. For example, using the api:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/v1/signup");
}
And remove that line from the HttpSecurity part. This will tell Spring Security to ignore this URL and don't apply any filters to them.
I have a better way:
http
.authorizeRequests()
.antMatchers("/api/v1/signup/**").permitAll()
.anyRequest().authenticated()

Postman showing HTML instead of JSON

I have a simple Spring Boot + Spring Security REST app with quotations. Only 3 endpoints for GET, POST, DELETE. Only moderator and admin accounts defined. GET rest method works fine - it shows list of quotations. The problem is with POST and DELETE methods. When I try to invoke them in Postman it returns HTML (logging form defined in SecurityConfig).
QuotationApi.java
#RestController
public class QuotationApi {
private List<Quotation> quotations;
public QuotationApi() {
this.quotations = new ArrayList<>();
quotations.add(new Quotation("Those who dare to fail miserably can achieve greatly.", "John F. Kennedy"));
quotations.add(new Quotation("Get busy living or get busy dying.", "Stephen King"));
}
#GetMapping("/api")
public List<Quotation> getQuotation() {
return quotations;
}
#PostMapping("/api")
public boolean addQuotation(#RequestBody Quotation quotation) {
return quotations.add(quotation);
}
#DeleteMapping("/api")
public void deleteQuotation(#RequestParam int index) {
quotations.remove(index);
}
}
SecurityConfig.java
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// creating users
#Bean
public UserDetailsService userDetailsService() {
UserDetails moderator = User.withDefaultPasswordEncoder()
.username("user")
.password("user")
.roles("MODERATOR")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(moderator, admin);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET,"/api").permitAll()
.antMatchers(HttpMethod.POST,"/api").hasRole("MODERATOR")
.antMatchers(HttpMethod.DELETE,"/api").hasRole("ADMIN")
.anyRequest().hasRole("ADMIN")
.and()
.formLogin().permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
}
I have Basic_auth in Postman:
EDIT after Andreas's help (working code):
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET,"/api").permitAll()
.antMatchers(HttpMethod.POST,"/api").hasRole("MODERATOR")
.antMatchers(HttpMethod.DELETE,"/api").hasRole("ADMIN")
.anyRequest().hasRole("ADMIN")
.and()
.httpBasic()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll()
.and()
.csrf().disable();
}
Doesn't matter that Postman is sending Basic authentication header, when you haven't enabled Basic authentication in Spring.
Since you only called formLogin() to enable form based authentication, you have to login using the form POST.
Of course, you could just call httpBasic() to enable Basic authentication too.

Spring throwing 403 Forbidden on web service to upload image

I have a controller to upload a user's avatar that has been working for quite a while. Until recently, after I made a change to my spring security configuration. The change was to have unauthorized calls to API's return 403 forbidden and unauthorized calls to anything else redirect to login. Since making this change the app throws a 403 every time a call is made to upload an avatar. Every other API works as intended.
Here are the snippets I believe are relevant to the issue at hand:
Controller:
#Controller
#RequestMapping("/api/users")
public class UsersController {
#RequestMapping(value = "/upload_avatar", params = { "filename" }, method = RequestMethod.POST)
public #ResponseBody ResponseStatusDTO handleFileUpload(
#RequestParam("file") MultipartFile file,
#RequestParam(value = "filename") String filename) {
if (!file.isEmpty()) {
try {
String newFilename = userUtil.uploadAvatar(file, filename);
return new ResponseStatusDTO(1, newFilename);
} catch (Exception e) {
return new ResponseStatusDTO(1, "Failed to upload " + filename
+ "!");
}
} else {
return new ResponseStatusDTO(1, "Failed to upload " + filename
+ " because the file was empty.");
}
}
}
Ajax Call Performing Request:
uploadAvatar : function(){
var file = this.getSelectedFile();
var data = new FormData();
data.append('file', file);
var name = file.name;
$.ajax({
url: './api/users/upload_avatar?filename='+ name,
data: data,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: _.bind(function(data){
this.avatar = data.message;
}, this),
error: _.bind(function(data){
//TODO
}, this)
});
}
Latest Spring Security Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Autowired
private CommonAuthenticationProvider authProvider;
#Autowired
AuthFailureHandler authFailureHandler;
#Autowired
AuthSuccessHandler authSuccessHandler;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authProvider);
}
#Configuration
#Order(1)
public static class ApiLoginWebSecurityConfigurationAdapter extends
WebSecurityConfigurerAdapter {
#Autowired
private Http403ForbiddenEntryPoint forbiddenEntryPoint;
#Bean
public Http403ForbiddenEntryPoint forbiddenEntryPoint() {
return new Http403ForbiddenEntryPoint();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.authenticationEntryPoint(forbiddenEntryPoint);
// #formatter:on
}
}
#Configuration
public static class FormLoginWebSecurityConfigurationAdapter extends
WebSecurityConfigurerAdapter {
#Autowired
AuthFailureHandler authFailureHandler;
#Autowired
AuthSuccessHandler authSuccessHandler;
#Autowired
private LoginUrlAuthenticationEntryPoint loginEntryPoint;
#Bean
public LoginUrlAuthenticationEntryPoint loginEntryPoint() {
return new LoginUrlAuthenticationEntryPoint("/login");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/webjars/**",
"/login/**", "/session/**", "/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("username")
.passwordParameter("password")
.failureHandler(authFailureHandler)
.successHandler(authSuccessHandler)
.permitAll()
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
// .deleteCookies(cookieNamesToClear)
.and()
.httpBasic().authenticationEntryPoint(loginEntryPoint)
.and()
.csrf().disable();
// #formatter:on
}
}
}
Finally found the issue after several hours. In the new api security configuration I didn't disable csrf and wasn't sending a token. Once I disabled csrf it worked as expected.
#Configuration
#Order(1)
public static class ApiLoginWebSecurityConfigurationAdapter extends
WebSecurityConfigurerAdapter {
#Autowired
private Http403ForbiddenEntryPoint forbiddenEntryPoint;
#Bean
public Http403ForbiddenEntryPoint forbiddenEntryPoint() {
return new Http403ForbiddenEntryPoint();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.antMatcher("/api/**")
.authorizeRequests()
.anyRequest().fullyAuthenticated()
.and()
.httpBasic()
.authenticationEntryPoint(forbiddenEntryPoint)
.and()
.csrf().disable();
// #formatter:on
}
}

Categories