Prechádzať zdrojové kódy

Centralized access policies and custom auth endpoint

Daniel Garcia Costa 2 rokov pred
rodič
commit
6c82b3c35c

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

@@ -5,6 +5,7 @@ import static org.springframework.security.config.http.SessionCreationPolicy.STA
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.ProviderManager;
 import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
@@ -44,13 +45,20 @@ public class WebSecurityConfig{
 	@Bean
 	public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
 		
+		CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter(authenticationManager(), jwtService);
+    	authenticationFilter.setFilterProcessesUrl("/api/v1/login");
+		
 		http.csrf().disable()
 			.cors().disable()
 			.sessionManagement().sessionCreationPolicy(STATELESS)
 			.and()
-			.authorizeHttpRequests().anyRequest().permitAll()
+			.authorizeHttpRequests()
+				.requestMatchers("/api/v1/login").permitAll()
+	    		.requestMatchers(HttpMethod.GET, "/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(new CustomAuthenticationFilter(authenticationManager(), jwtService));
+			.addFilter(authenticationFilter);
 		
 		return http.build();
 	}

+ 67 - 0
src/main/java/es/uv/garcosda/endpoints/UserRestController.java

@@ -0,0 +1,67 @@
+package es.uv.garcosda.endpoints;
+
+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;
+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)
+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); 
+			}
+		}
+		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); 
+			}
+		}
+		return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
+	}
+}

+ 44 - 0
src/main/java/es/uv/garcosda/security/RestAuthenticationSuccessHandler.java

@@ -0,0 +1,44 @@
+package es.uv.garcosda.security;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@Component
+public class RestAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler 
+{
+
+    private final ObjectMapper mapper;
+
+    @Autowired
+    public RestAuthenticationSuccessHandler(MappingJackson2HttpMessageConverter messageConverter) {
+        this.mapper = messageConverter.getObjectMapper();
+    }
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest request, 
+							    		HttpServletResponse response,
+							            Authentication authentication) 
+							          throws IOException, ServletException {
+        response.setStatus(HttpServletResponse.SC_OK);
+
+        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
+        PrintWriter writer = response.getWriter();
+        mapper.writeValue(writer, userDetails);
+        writer.flush();
+        writer.close();
+    }
+}
+

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

@@ -21,9 +21,10 @@ INSERT INTO user_role(user_id, role_id) VALUES
 (1,1),
 (1,2),
 (1,3),
-(3,2);
+(3,2),
+(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());
+(3,'Securing Web apps using SpringSecurity','Spring Security provides Authentication and Authorization support.', '2017-04-20', now());