I'm trying to restrict access to an endpoint based on roles. I searched here on stackoverflow for a solution and tried a few but was not successful in solving my problem. I tried to make the restriction both in the global configuration (WebSecurityConfig) and also with PreAuthorize Annotation, both returned 403 when called. My test user has the SYS_ADMIN role, I have already validated this flow and it actually has the role. Below are my sources.
Global WebSecurityConfig
@Configuration
@EnableWebSecurity
@EnableAutoConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtUserDetailsService;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
@Override
@Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.headers().frameOptions().disable();
httpSecurity.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/spring-security-rest/**").permitAll().and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/authentication/**").permitAll()
.antMatchers(HttpMethod.POST, "/api/persons/**").hasRole("SYS_ADMIN")
.antMatchers(HttpMethod.GET, "/api/roles/**").permitAll()
.anyRequest().authenticated().and()
.headers().frameOptions().sameOrigin().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().mvcMatchers(HttpMethod.OPTIONS, "/**");
web.ignoring().mvcMatchers("/swagger-ui.html/**", "/configuration/**", "/swagger-resources/**", "/v2/api-docs",
"/webjars/**");
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Person Controller
package br.com.aquamain.backend.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import br.com.aquamain.backend.exception.InvalidPersonException;
import br.com.aquamain.backend.model.CustomPage;
import br.com.aquamain.backend.model.Person;
import br.com.aquamain.backend.service.PersonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/api/persons")
@Api(tags = "Persons")
public class PersonController {
@Autowired
private PersonService personService;
...
@PostMapping(path = "new", consumes = "application/json")
@ApiOperation(value = "Create a new person.")
public ResponseEntity<?> save(@RequestBody Person person) {
try {
CustomPage<Person> persons = this.personService.save(person);
return new ResponseEntity<CustomPage<Person>>(persons, HttpStatus.CREATED);
} catch (InvalidPersonException error) {
return new ResponseEntity<String>(error.getMessage(), HttpStatus.BAD_REQUEST);
}
}
...
}
Role Model
package br.com.aquamain.backend.model;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@Entity
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
@Table(name = "AQUARIUM_MAINTANCE_MONITOR_ROLE")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ID_ROLE", nullable = false, precision = 9, scale = 0)
private Integer id;
@JsonIgnoreProperties(value = {"roles"})
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "roles")
@JsonInclude(Include.NON_NULL)
private List<User> users;
@Column(name = "CODE_ROLE", nullable = true, length = 255)
private String code;
@Column(name = "DESCRIPTION_ROLE", nullable = true, length = 255)
private String description;
@Column(name = "NAME_ROLE", nullable = true, length = 255)
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
CodePudding user response:
The role permissions on your control are correct.
@Secured( {"ROLE_myAuthority"} )
You had, in fact, given the proper approval.
new SimpleGrantedAuthority("ROLE_myAuthority");
The UsernamePasswordAuthenticationToken object really grants authority.
Filter is been injected correctly
Authentication auth = new UsernamePasswordAuthenticationToken(username, authentication.getCredentials(), authorities);
Collection<? extends GrantedAuthority> auths = auth.getAuthorities();`
Iterator authsIterator = auths.iterator();
while (authsIterator.hasNext()) {
SimpleGrantedAuthority sga = (SimpleGrantedAuthority) authsIterator.next();
sga.getAuthority();
// ...
}
CodePudding user response:
Your are using .hasRole("SYS_ADMIN")
instead of this try .hasAnyAuthority("SYS_ADMIN")
this worked for me and also give @EnableWebMvc
annotation in config class.
Also check pom.xml file for below dependency.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.6.6</version>
</dependency>