I am working on a simple project with 2 defined roles Admin and User,The admin role has authority to view users by thier usernames but when I login as an admin and i hit the /admin/users/usernames/{username}
I get 401 Unauthenticated you can see the request here postman request
WHATS THE PROBLEM WITH THE FOLLOWING IMPLEMENTATION?
**The login Method **
@Override
public ResponseEntity<User> login(String username, String password) {
authenticate(username,password);
User loginUser = findUserByUsername(username);
UserPrinciple userPrinciple = new UserPrinciple(loginUser);
HttpHeaders tokenHeader = getJwtToken(userPrinciple);
return new ResponseEntity<>(loginUser,tokenHeader, HttpStatus.OK);
}
private void authenticate(String username, String password) {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username,password));
}
private HttpHeaders getJwtToken(UserPrinciple userPrinciple) {
String token= jwtTokenProvider.generateJWTToken(userPrinciple);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(SecurityConsts.JWT_TOKEN_HEADER,token);
return httpHeaders;
}
The User ReST Controlle
@RestController
@RequestMapping("/admin/users")
public class UserController {
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/")
public List<User> getUsers() {
return userService.getUsers();
}
@GetMapping("/emails/{email}")
public User findUserByEmail(@PathVariable String email) {
return userService.findUserByEmail(email);
}
@GetMapping("/usernames/{username}")
public User findUserByUsername(@PathVariable String username) {
return userService.findUserByUsername(username);
}
@JsonView(BodyView.BasicUser.class)
@PostMapping("/register")
public User addUser(@RequestBody User user) throws UsernameExistsException {
return userService.addUser(user);
}
@JsonView(BodyView.BasicUser.class)
@PostMapping("/login")
public ResponseEntity<User> login(@RequestBody User user) {
return userService.login(user.getUsername(), user.getPassword());
}
}
POM.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>FM6-App</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>FM6-App</name>
<description>FM6-App</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.19.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
The Web Security Configurer Adapter
The public URLS:
public static final String[] PUBLIC_URLS ={"/users/admin/login/**","/admin/users/register/**","/demandes/**","/criteres/**"};
@Configuration
@EnableWebSecurity
@ComponentScan
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JWTAuthorizationFilter jwtAuthorizationFilter;
@Autowired
private JWTAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JWTAccessDenielHandler jwtAccessDenielHandler;
@Autowired
private UserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/admin/users/login/**").permitAll()
.antMatchers(SecurityConsts.PUBLIC_URLS).permitAll()
.antMatchers("/admin/**").hasAnyAuthority("ROLE_ADMIN")
.antMatchers("/utilisateur/**").hasAnyAuthority("ROLE_USER")
// .anyRequest().authenticated()
.and()
.exceptionHandling().accessDeniedHandler(jwtAccessDenielHandler)
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder);
}
@Bean(name = "authenticationManager")
@Override
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManagerBean();
}
JWT Authorization Filter
@Component
public class JWTAuthorizationFilter extends OncePerRequestFilter {
@Autowired
private JWTTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if(request.getMethod().equalsIgnoreCase(SecurityConsts.OPTION_HTTP_METHOD)){
response.setStatus(SecurityConsts.OK.value());
}else if(request.getRequestURI().equals("/users/login")){
filterChain.doFilter(request,response);
}
else {
String autorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if(autorizationHeader == null || !autorizationHeader.startsWith(SecurityConsts.TOKEN_PREFIX)){
filterChain.doFilter(request,response);
return;
}
String token = autorizationHeader.substring(SecurityConsts.TOKEN_PREFIX.length());
String username = jwtTokenProvider.getSubject(token);
if(jwtTokenProvider.isTokenValid(username,token) && SecurityContextHolder.getContext().getAuthentication() == null){
List<GrantedAuthority> authorities = jwtTokenProvider.getAuthorities(token);
Authentication authentication = jwtTokenProvider.getAuthentication(username,authorities,request);
SecurityContextHolder.getContext().setAuthentication(authentication);
}else {
SecurityContextHolder.clearContext();
}
}
filterChain.doFilter(request,response);
}
}
The Cross-Origin Configuration class
@Configuration
public class CorsConfig {
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH")
.allowedHeaders("*")
.allowedOrigins("*");
}
};
}
}
CodePudding user response:
Can you debug the application? The error you mentioned in the comments javax.management.InstanceNotFoundException: org.springframework.boot:type=Admin,name=SpringApplication
is IDE-related, there is stackoverflow post about it, so this is not the issue.
Try to debug code and check:
- If you, actually, get to
JWTAuthorizationFilter
- If your user is identified as admin user here:
List<GrantedAuthority> authorities = jwtTokenProvider.getAuthorities(token);
- Check if you pass a correct and the newest token to Postman in
Authorization
header; check url (in the screenshot you have provided url is incorrect, it isuseErnames
) - What is the value of
SecurityConsts.TOKEN_PREFIX
? It should include space, i.e. 'Bearer '
CodePudding user response:
I can't really see what is the problem with your code, but try to add this method in your WebSecurityConfig class:
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.GET, "/admin/**");
web.ignoring().antMatchers(HttpMethod.POST, "/admin/**");
}