Bladeren bron

Authorize request with JWT

Daniel Garcia Costa 2 jaren geleden
bovenliggende
commit
8edb5a7863

+ 9 - 2
src/main/java/es/uv/garcosda/config/WebSecurityConfig.java

@@ -14,8 +14,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
 import es.uv.garcosda.security.CustomAuthenticationFilter;
+import es.uv.garcosda.security.CustomAuthorizationFilter;
 import es.uv.garcosda.security.CustomUserDetailsService;
 import es.uv.garcosda.services.JwtService;
 
@@ -47,6 +49,8 @@ public class WebSecurityConfig{
 		
 		CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter(authenticationManager(), jwtService);
     	authenticationFilter.setFilterProcessesUrl("/api/v1/login");
+    	
+    	CustomAuthorizationFilter authorizationFilter = new CustomAuthorizationFilter(jwtService);
 		
 		http.csrf().disable()
 			.cors().disable()
@@ -54,11 +58,14 @@ public class WebSecurityConfig{
 			.and()
 			.authorizeHttpRequests()
 				.requestMatchers("/api/v1/login").permitAll()
+				.requestMatchers(HttpMethod.GET, "/api/v1/users/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
+				.requestMatchers(HttpMethod.GET, "/api/v1/posts").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
 	    		.requestMatchers(HttpMethod.GET, "/api/v1/posts/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
-	    		.requestMatchers(HttpMethod.POST, "/api/v1/posts/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
+	    		.requestMatchers(HttpMethod.POST, "/api/v1/posts").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
 	    		.requestMatchers(HttpMethod.DELETE, "/api/v1/posts/**").hasAuthority("ROLE_ADMIN")
 			.and()
-			.addFilter(authenticationFilter);
+			.addFilter(authenticationFilter)
+			.addFilterBefore(authorizationFilter, UsernamePasswordAuthenticationFilter.class);
 		
 		return http.build();
 	}

+ 3 - 1
src/main/java/es/uv/garcosda/domain/Post.java

@@ -9,6 +9,7 @@ import jakarta.persistence.GeneratedValue;
 import jakarta.persistence.GenerationType;
 import jakarta.persistence.Id;
 import jakarta.persistence.Lob;
+import jakarta.persistence.SequenceGenerator;
 import jakarta.persistence.Table;
 import jakarta.persistence.Temporal;
 import jakarta.persistence.TemporalType;
@@ -21,7 +22,8 @@ public class Post implements Serializable {
 	private static final long serialVersionUID = 1L;
 	
 	@Id
-	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	@GeneratedValue(strategy=GenerationType.AUTO)
+	@SequenceGenerator(name="id_post_generator", sequenceName = "id_post_seq", allocationSize=50, initialValue=4)
 	@Column(name = "post_id")
 	private Integer id;
 	

+ 0 - 7
src/main/java/es/uv/garcosda/endpoints/BlogRestController.java

@@ -8,8 +8,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.security.access.prepost.PostAuthorize;
-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;
@@ -32,7 +30,6 @@ public class BlogRestController {
 	@Autowired private BlogService blogService;
 	
 	@GetMapping("posts")
-	@PreAuthorize("permitAll")
 	public PostsResponseDTO findPosts(@RequestBody PostsRequestDTO request) {
 		LOGGER.debug("View all posts");
 		Page<Post> pageData = blogService.findPosts(request);
@@ -41,8 +38,6 @@ public class BlogRestController {
 	}
 	
 	@GetMapping("posts/{id}")
-	@PreAuthorize("isAuthenticated() and #postId < 10")
-	@PostAuthorize("returnObject.isPresent() and returnObject.get().id >= 1")
 	public Optional<Post> findPostById(@PathVariable("id") Integer id) {
 		LOGGER.debug("View Post id: "+id);
 		Optional<Post> post = blogService.findPostById(id);
@@ -50,7 +45,6 @@ public class BlogRestController {
 	}
 	
 	@PostMapping("posts")
-	@PreAuthorize("hasRole('ADMIN') OR hasRole('USER')")
 	public ResponseEntity<Post> createPost(@RequestBody Post post) {
 		LOGGER.debug("Create post");
 		Post createdPost = blogService.createPost(post);
@@ -58,7 +52,6 @@ public class BlogRestController {
 	}
 			
 	@DeleteMapping("posts/{id}")
-	@PreAuthorize("hasRole('ADMIN')")
 	public void deletePostById(@PathVariable("id") Integer id) {
 		LOGGER.debug("Delete Post id: "+id);
 		blogService.deletePost(id);

+ 21 - 37
src/main/java/es/uv/garcosda/endpoints/UserRestController.java

@@ -4,12 +4,9 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
-import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -18,49 +15,36 @@ import org.springframework.web.bind.annotation.RestController;
 import es.uv.garcosda.models.AuthenticatedUser;
 
 @RestController
-@PreAuthorize("isAuthenticated()")
-@RequestMapping(value="/api/v1/users", produces=MediaType.APPLICATION_JSON_VALUE)
+@RequestMapping("/api/v1/users")
 public class UserRestController {
 	
 	@GetMapping("authenticated")
-	public ResponseEntity<AuthenticatedUser> getAuthenticatedUser() {
-		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-		if(authentication!= null) {
-			Object userDetails = authentication.getPrincipal();
-			if(userDetails != null && userDetails instanceof UserDetails)
-			{
-				UserDetails secUser = (UserDetails) userDetails;
-				String username = secUser.getUsername();
-				
-				List<String> roles = secUser.getAuthorities()
-											.stream()
-												.map(authority -> authority.getAuthority())
-												.collect(Collectors.toList());
-				AuthenticatedUser authenticatedUser = new AuthenticatedUser(username, roles);
-				return new ResponseEntity<>(authenticatedUser,HttpStatus.OK); 
-			}
+	public ResponseEntity<AuthenticatedUser> getAuthenticatedUser(Authentication auth) {		
+		if(auth.isAuthenticated()) {
+			String username = auth.getName();
+			List<String> roles = auth.getAuthorities()
+									 .stream()
+										 .map(authority -> authority.getAuthority())
+									 	 .collect(Collectors.toList());
+			AuthenticatedUser authenticatedUser = new AuthenticatedUser(username, roles);
+			return new ResponseEntity<>(authenticatedUser,HttpStatus.OK);
 		}
 		return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
 	}
 	
 
 	
-	@GetMapping("roles/{username}")
-	@PreAuthorize("#username == authentication.principal.username")
-	public ResponseEntity<AuthenticatedUser> getMyRoles(@PathVariable String username) {
-		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-		if(authentication!= null){
-			Object userDetails = authentication.getPrincipal();
-			if(userDetails != null && userDetails instanceof UserDetails){
-				UserDetails secUser = (UserDetails) userDetails;
-				
-				List<String> roles = secUser.getAuthorities()
-											.stream()
-												.map(authority -> authority.getAuthority())
-												.collect(Collectors.toList());
-				AuthenticatedUser authenticatedUser = new AuthenticatedUser(username, roles);
-				return new ResponseEntity<>(authenticatedUser,HttpStatus.OK); 
-			}
+	@GetMapping("roles/{username_}")
+	@PreAuthorize("#username_ == authentication.principal.username")
+	public ResponseEntity<AuthenticatedUser> getMyRoles(@PathVariable String username_, Authentication auth) {
+		if(auth.isAuthenticated()) {
+			String username = auth.getName();
+			List<String> roles = auth.getAuthorities()
+									 .stream()
+										 .map(authority -> authority.getAuthority())
+									 	 .collect(Collectors.toList());
+			AuthenticatedUser authenticatedUser = new AuthenticatedUser(username, roles);
+			return new ResponseEntity<>(authenticatedUser,HttpStatus.OK);
 		}
 		return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
 	}

+ 68 - 0
src/main/java/es/uv/garcosda/security/CustomAuthorizationFilter.java

@@ -0,0 +1,68 @@
+package es.uv.garcosda.security;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.http.MediaType;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import es.uv.garcosda.services.JwtService;
+
+import static org.springframework.http.HttpHeaders.AUTHORIZATION;
+
+public class CustomAuthorizationFilter extends OncePerRequestFilter {
+
+	private JwtService jwtService;
+	
+	public CustomAuthorizationFilter(JwtService jwtService) {
+		this.jwtService = jwtService;
+	}
+	
+	@Override
+	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+		if(request.getServletPath().equals("/api/v1/login")) {
+			filterChain.doFilter(request, response);
+		}
+		else {		
+			String authHeader = request.getHeader(AUTHORIZATION);
+			if(authHeader != null && authHeader.startsWith("Bearer ")) {
+				try {
+					String token = jwtService.getTokenFromHeader(authHeader);
+					String username = jwtService.getUsernameFromToken(token);		
+					
+					Collection<SimpleGrantedAuthority> authorities = jwtService.getAuthoritiesFromToken(token);
+					UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
+					SecurityContextHolder.getContext().setAuthentication(authenticationToken);
+					filterChain.doFilter(request, response);
+				}
+				catch(Exception exception) {
+					response.setHeader("error", exception.getMessage());
+					response.setStatus(403);
+					System.out.println(403);
+					Map<String, String> error = new HashMap<>();
+					error.put("error_msg", exception.getMessage());
+					response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+					new ObjectMapper().writeValue(response.getOutputStream(), error);
+				}
+			}
+			else {
+				filterChain.doFilter(request, response);
+			}
+		}
+	}
+
+	
+	
+}

+ 4 - 3
src/main/resources/data.sql

@@ -25,6 +25,7 @@ INSERT INTO user_role(user_id, role_id) VALUES
 (3,3);
 
 INSERT INTO posts(post_id, title, content, created_on, updated_on) VALUES
-(1,'Introducing SpringBoot','SpringBoot is an opinionated approach for building Spring applications.', '2017-06-20', null),
-(2,'CRUD using Spring Data JPA','Spring Data JPA provides JpaRepository which can be extended to have CRUD operations', '2017-06-25', null),
-(3,'Securing Web apps using SpringSecurity','Spring Security provides Authentication and Authorization support.', '2017-04-20', now());
+(100, 'Introducing SpringBoot','SpringBoot is an opinionated approach for building Spring applications.', '2017-06-20', null),
+(101, 'CRUD using Spring Data JPA','Spring Data JPA provides JpaRepository which can be extended to have CRUD operations', '2017-06-25', null),
+(102, 'Securing Web apps using SpringSecurity','Spring Security provides Authentication and Authorization support.', '2017-04-20', now());
+