From c6c70ca2f588698ba67aea7263686377fde75d2e Mon Sep 17 00:00:00 2001 From: Kama-Pushka Date: Fri, 25 Apr 2025 18:23:45 +0500 Subject: [PATCH 1/2] security --- build.gradle.kts | 3 + .../config/SecurityConfig.java | 66 +++++++++++++++++++ .../controller/ApiController.java | 31 +++++++++ .../org/javaspringcourse/domain/UserRole.java | 13 ++++ .../org/javaspringcourse/dto/UserInfo.java | 5 ++ 5 files changed, 118 insertions(+) create mode 100644 src/main/java/org/javaspringcourse/config/SecurityConfig.java create mode 100644 src/main/java/org/javaspringcourse/controller/ApiController.java create mode 100644 src/main/java/org/javaspringcourse/domain/UserRole.java create mode 100644 src/main/java/org/javaspringcourse/dto/UserInfo.java diff --git a/build.gradle.kts b/build.gradle.kts index fc5abdc..a3c5ea1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,7 +19,10 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springframework.boot:spring-boot-starter-web") testImplementation("org.springframework.boot:spring-boot-starter-test") + testImplementation("org.springframework.security:spring-security-test") testRuntimeOnly("org.junit.platform:junit-platform-launcher") } diff --git a/src/main/java/org/javaspringcourse/config/SecurityConfig.java b/src/main/java/org/javaspringcourse/config/SecurityConfig.java new file mode 100644 index 0000000..9bc0c6b --- /dev/null +++ b/src/main/java/org/javaspringcourse/config/SecurityConfig.java @@ -0,0 +1,66 @@ +package org.javaspringcourse.config; + +import org.javaspringcourse.domain.UserRole; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + private static final String[] PUBLIC_URLS = { + "/public/**" + }; + private static final String[] ADMIN_URLS = { + "/admin/**" + }; + private static final String[] SUPPORT_URLS = { + "/support/**" + }; + + @Bean + public SecurityFilterChain configure(HttpSecurity http) throws Exception { + return http.csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .logout(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(c -> c + .requestMatchers(PUBLIC_URLS) + .permitAll() + .requestMatchers(ADMIN_URLS) + .hasAuthority(UserRole.ADMIN.name()) + .requestMatchers(SUPPORT_URLS) + .hasAuthority(UserRole.SUPPORT.name())) + .httpBasic(_ -> {}) + .build(); + } + + @Bean + public PasswordEncoder bCryptPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { + var admin = User.builder() + .username("admin") + .password(passwordEncoder.encode("maozedong")) + .authorities(UserRole.ADMIN) + .build(); + + var support = User.builder() + .username("support") + .password(passwordEncoder.encode("password")) + .authorities(UserRole.SUPPORT) + .build(); + + return new InMemoryUserDetailsManager(admin, support); + } +} diff --git a/src/main/java/org/javaspringcourse/controller/ApiController.java b/src/main/java/org/javaspringcourse/controller/ApiController.java new file mode 100644 index 0000000..e2fb87c --- /dev/null +++ b/src/main/java/org/javaspringcourse/controller/ApiController.java @@ -0,0 +1,31 @@ +package org.javaspringcourse.controller; + +import org.javaspringcourse.dto.UserInfo; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ApiController { + + @GetMapping("/public/api") + public String publicApi() { + return "Hello World"; + } + + @GetMapping("/admin/api") + public UserInfo getAdminInfo(@AuthenticationPrincipal UserDetails user) { + return new UserInfo(user.getUsername(), user.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .toList()); + } + + @GetMapping("/support/api") + public UserInfo getSupportUserInfo(@AuthenticationPrincipal UserDetails user) { + return new UserInfo(user.getUsername(), user.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .toList()); + } +} diff --git a/src/main/java/org/javaspringcourse/domain/UserRole.java b/src/main/java/org/javaspringcourse/domain/UserRole.java new file mode 100644 index 0000000..7f7deed --- /dev/null +++ b/src/main/java/org/javaspringcourse/domain/UserRole.java @@ -0,0 +1,13 @@ +package org.javaspringcourse.domain; + +import org.springframework.security.core.GrantedAuthority; + +public enum UserRole implements GrantedAuthority { + ADMIN, + SUPPORT; + + @Override + public String getAuthority() { + return this.name(); + } +} diff --git a/src/main/java/org/javaspringcourse/dto/UserInfo.java b/src/main/java/org/javaspringcourse/dto/UserInfo.java new file mode 100644 index 0000000..796e868 --- /dev/null +++ b/src/main/java/org/javaspringcourse/dto/UserInfo.java @@ -0,0 +1,5 @@ +package org.javaspringcourse.dto; + +import java.util.List; + +public record UserInfo(String username, List roles) {} \ No newline at end of file From c6b8af1cc1336e195298fcec7bc7b4b6a493ccd1 Mon Sep 17 00:00:00 2001 From: Kama-Pushka Date: Mon, 28 Apr 2025 13:34:49 +0500 Subject: [PATCH 2/2] fix roles --- .../java/org/javaspringcourse/config/SecurityConfig.java | 8 ++++---- src/main/java/org/javaspringcourse/domain/UserRole.java | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/javaspringcourse/config/SecurityConfig.java b/src/main/java/org/javaspringcourse/config/SecurityConfig.java index 9bc0c6b..d954b0c 100644 --- a/src/main/java/org/javaspringcourse/config/SecurityConfig.java +++ b/src/main/java/org/javaspringcourse/config/SecurityConfig.java @@ -35,9 +35,9 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception { .requestMatchers(PUBLIC_URLS) .permitAll() .requestMatchers(ADMIN_URLS) - .hasAuthority(UserRole.ADMIN.name()) + .hasAuthority(UserRole.ROLE_ADMIN.name()) .requestMatchers(SUPPORT_URLS) - .hasAuthority(UserRole.SUPPORT.name())) + .hasAuthority(UserRole.ROLE_SUPPORT.name())) .httpBasic(_ -> {}) .build(); } @@ -52,13 +52,13 @@ public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { var admin = User.builder() .username("admin") .password(passwordEncoder.encode("maozedong")) - .authorities(UserRole.ADMIN) + .authorities(UserRole.ROLE_ADMIN) .build(); var support = User.builder() .username("support") .password(passwordEncoder.encode("password")) - .authorities(UserRole.SUPPORT) + .authorities(UserRole.ROLE_SUPPORT) .build(); return new InMemoryUserDetailsManager(admin, support); diff --git a/src/main/java/org/javaspringcourse/domain/UserRole.java b/src/main/java/org/javaspringcourse/domain/UserRole.java index 7f7deed..cd897f6 100644 --- a/src/main/java/org/javaspringcourse/domain/UserRole.java +++ b/src/main/java/org/javaspringcourse/domain/UserRole.java @@ -3,8 +3,8 @@ import org.springframework.security.core.GrantedAuthority; public enum UserRole implements GrantedAuthority { - ADMIN, - SUPPORT; + ROLE_ADMIN, + ROLE_SUPPORT; @Override public String getAuthority() {