diff --git a/spring-security/step-1/src/main/java/com/spring/SpringSecurityApplication.java b/spring-security/official/src/main/java/com/spring/SpringSecurityOfficialApplication.java similarity index 63% rename from spring-security/step-1/src/main/java/com/spring/SpringSecurityApplication.java rename to spring-security/official/src/main/java/com/spring/SpringSecurityOfficialApplication.java index 7cb2a3c..e0c684c 100644 --- a/spring-security/step-1/src/main/java/com/spring/SpringSecurityApplication.java +++ b/spring-security/official/src/main/java/com/spring/SpringSecurityOfficialApplication.java @@ -4,8 +4,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class SpringSecurityApplication { +public class SpringSecurityOfficialApplication { public static void main(String[] args) { - SpringApplication.run(SpringSecurityApplication.class, args); + SpringApplication.run(SpringSecurityOfficialApplication.class, args); } } diff --git a/spring-security/official/src/main/resources/application.yml b/spring-security/official/src/main/resources/application.yml index 0bee250..bd567d1 100644 --- a/spring-security/official/src/main/resources/application.yml +++ b/spring-security/official/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 8778 + port: 8770 spring: application: diff --git a/spring-security/official/src/main/resources/static/favicon.ico b/spring-security/official/src/main/resources/static/favicon.ico index 17f9dd1..385f8a6 100644 Binary files a/spring-security/official/src/main/resources/static/favicon.ico and b/spring-security/official/src/main/resources/static/favicon.ico differ diff --git a/spring-security/step-1/src/test/java/com/spring/SpringSecurityApplicationTests.java b/spring-security/official/src/test/java/com/spring/SpringSecurityOfficialApplicationTests.java similarity index 79% rename from spring-security/step-1/src/test/java/com/spring/SpringSecurityApplicationTests.java rename to spring-security/official/src/test/java/com/spring/SpringSecurityOfficialApplicationTests.java index 1463f61..9d5eea1 100644 --- a/spring-security/step-1/src/test/java/com/spring/SpringSecurityApplicationTests.java +++ b/spring-security/official/src/test/java/com/spring/SpringSecurityOfficialApplicationTests.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class SpringSecurityApplicationTests { +class SpringSecurityOfficialApplicationTests { @Test void contextLoads() { diff --git a/spring-security/pom.xml b/spring-security/pom.xml index f751b66..90ca1ba 100644 --- a/spring-security/pom.xml +++ b/spring-security/pom.xml @@ -14,7 +14,7 @@ 0.0.1-SNAPSHOT spring-security spring-security - + pom @@ -99,6 +99,14 @@ + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity6 + org.webjars bootstrap @@ -114,14 +122,6 @@ jquery ${jquery.version} - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.thymeleaf.extras - thymeleaf-extras-springsecurity6 - diff --git a/spring-security/step-1/pom.xml b/spring-security/step-1/pom.xml index 1e76b55..538b5b2 100644 --- a/spring-security/step-1/pom.xml +++ b/spring-security/step-1/pom.xml @@ -22,6 +22,6 @@ - + diff --git a/spring-security/official/src/main/java/com/spring/SpringSecurityApplication.java b/spring-security/step-1/src/main/java/com/spring/SpringSecurityStep1Application.java similarity index 64% rename from spring-security/official/src/main/java/com/spring/SpringSecurityApplication.java rename to spring-security/step-1/src/main/java/com/spring/SpringSecurityStep1Application.java index 7cb2a3c..d068ecb 100644 --- a/spring-security/official/src/main/java/com/spring/SpringSecurityApplication.java +++ b/spring-security/step-1/src/main/java/com/spring/SpringSecurityStep1Application.java @@ -4,8 +4,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class SpringSecurityApplication { +public class SpringSecurityStep1Application { public static void main(String[] args) { - SpringApplication.run(SpringSecurityApplication.class, args); + SpringApplication.run(SpringSecurityStep1Application.class, args); } } diff --git a/spring-security/step-1/src/main/java/com/spring/config/MD5PasswordEncoder.java b/spring-security/step-1/src/main/java/com/spring/config/MD5PasswordEncoder.java deleted file mode 100644 index 8135132..0000000 --- a/spring-security/step-1/src/main/java/com/spring/config/MD5PasswordEncoder.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.spring.config; - -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.util.DigestUtils; - -import java.util.Arrays; - -public class MD5PasswordEncoder implements PasswordEncoder { - @Override - public String encode(CharSequence rawPassword) { - String rawPasswordString = rawPassword.toString(); - byte[] md5Digest = DigestUtils.md5Digest(rawPasswordString.getBytes()); - return Arrays.toString(md5Digest); - } - - @Override - public boolean matches(CharSequence rawPassword, String encodedPassword) { - return false; - } -} diff --git a/spring-security/step-1/src/main/java/com/spring/config/security/SecurityWebConfiguration.java b/spring-security/step-1/src/main/java/com/spring/config/security/SecurityWebConfiguration.java deleted file mode 100644 index c1c9538..0000000 --- a/spring-security/step-1/src/main/java/com/spring/config/security/SecurityWebConfiguration.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.spring.config.security; - -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - -@EnableWebSecurity -@Configuration -public class SecurityWebConfiguration { -} diff --git a/spring-security/step-1/src/main/java/com/spring/controller/security/LoginController.java b/spring-security/step-1/src/main/java/com/spring/controller/security/LoginController.java index 8bbd819..2b75bae 100644 --- a/spring-security/step-1/src/main/java/com/spring/controller/security/LoginController.java +++ b/spring-security/step-1/src/main/java/com/spring/controller/security/LoginController.java @@ -1,35 +1,18 @@ package com.spring.controller.security; -import com.spring.domain.dto.security.LoginRequest; -import com.spring.domain.vo.result.Result; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; -@Tag(name = "Login接口", description = "登录接口") -@RestController -@RequestMapping("/api/security") -@RequiredArgsConstructor +@Controller public class LoginController { - private final AuthenticationManager authenticationManager; + @GetMapping("") + public String indexPage() { + return "index"; + } - @Operation(summary = "登录接口", description = "系统登录接口") - @PostMapping("login") - public Result login(@RequestBody LoginRequest loginRequest) { - String username = loginRequest.getUsername(); - String password = loginRequest.getPassword(); - - Authentication authenticationRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password); - - Authentication authenticationResponse = authenticationManager.authenticate(authenticationRequest); - return Result.success(authenticationResponse); + @GetMapping("/login-page") + public String showLoginPage() { + return "login"; } } \ No newline at end of file diff --git a/spring-security/step-1/src/main/java/com/spring/config/security/SecurityConfiguration.java b/spring-security/step-1/src/main/java/com/spring/security/SecurityConfiguration.java similarity index 70% rename from spring-security/step-1/src/main/java/com/spring/config/security/SecurityConfiguration.java rename to spring-security/step-1/src/main/java/com/spring/security/SecurityConfiguration.java index 1180368..b91b783 100644 --- a/spring-security/step-1/src/main/java/com/spring/config/security/SecurityConfiguration.java +++ b/spring-security/step-1/src/main/java/com/spring/security/SecurityConfiguration.java @@ -1,27 +1,17 @@ -package com.spring.config.security; +package com.spring.security; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 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.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; 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 public class SecurityConfiguration { - @Bean - SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(authorizeRequests -> - authorizeRequests.anyRequest().authenticated() - ) - ; - return http.build(); - } /** * 添加内存用户 @@ -31,10 +21,15 @@ public class SecurityConfiguration { @Bean @ConditionalOnMissingBean(UserDetailsService.class) InMemoryUserDetailsManager inMemoryUserDetailsManager(PasswordEncoder passwordEncoder) { - + // 使用注入的密码加密器进行密码加密 String generatedPassword = passwordEncoder.encode("123456"); - return new InMemoryUserDetailsManager(User.withUsername("bunny") - .password(generatedPassword).roles("USER").build()); + + // 创建用户 + UserDetails userDetails1 = User.withUsername("bunny").password(generatedPassword).roles("USER").build(); + UserDetails userDetails2 = User.withUsername("rabbit").password(generatedPassword).roles("USER").build(); + + // 返回内存中的用户 + return new InMemoryUserDetailsManager(userDetails1, userDetails2); } /** @@ -51,5 +46,8 @@ public class SecurityConfiguration { @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); + + // 自定义实现密码加密器 + // return new MD5PasswordEncoder(); } } diff --git a/spring-security/step-1/src/main/java/com/spring/security/SecurityWebConfiguration.java b/spring-security/step-1/src/main/java/com/spring/security/SecurityWebConfiguration.java new file mode 100644 index 0000000..bcc3990 --- /dev/null +++ b/spring-security/step-1/src/main/java/com/spring/security/SecurityWebConfiguration.java @@ -0,0 +1,49 @@ +package com.spring.security; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.web.SecurityFilterChain; + +@EnableMethodSecurity +@EnableWebSecurity +@Configuration +public class SecurityWebConfiguration { + + @Bean + SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + String[] permitAllUrls = { + "/", "/doc.html/**", + "/webjars/**", "/images/**", ".well-known/**", "favicon.ico", "/error/**", + "/v3/api-docs/**" + }; + + http.authorizeHttpRequests(authorizeRequests -> + // 访问路径为 /api/** 时需要进行认证 + authorizeRequests + .requestMatchers("/api/**").authenticated() + .requestMatchers(permitAllUrls).permitAll() + ) + .formLogin(loginPage -> loginPage + // 自定义登录页路径 + .loginPage("/login-page") + // 处理登录的URL(默认就是/login) + .loginProcessingUrl("/login") + // 登录成功跳转 + .defaultSuccessUrl("/") + // 登录失败跳转 + .failureUrl("/login-page?error=true") + .permitAll() + ) + // 使用默认的登录 + // .formLogin(Customizer.withDefaults()) + .logout(logout -> logout + .logoutSuccessUrl("/login-page?logout=true") + .permitAll() + ); + return http.build(); + } + +} diff --git a/spring-security/step-1/src/main/java/com/spring/security/password/MD5PasswordEncoder.java b/spring-security/step-1/src/main/java/com/spring/security/password/MD5PasswordEncoder.java new file mode 100644 index 0000000..d60e33a --- /dev/null +++ b/spring-security/step-1/src/main/java/com/spring/security/password/MD5PasswordEncoder.java @@ -0,0 +1,53 @@ +package com.spring.security.password; + +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.util.DigestUtils; +import org.springframework.util.StringUtils; + +import java.util.HexFormat; + +/** + *

MD5密码编码器实现

+ * + * 安全警告:此类使用MD5算法进行密码哈希,已不再安全,不推荐用于生产环境。 + * + *

MD5算法因其计算速度快且易受彩虹表攻击而被认为不安全。即使密码哈希本身是单向的, + * 但现代计算能力使得暴力破解和预先计算的彩虹表攻击变得可行。

+ * + *

Spring Security推荐使用BCrypt、PBKDF2、Argon2或Scrypt等自适应单向函数替代MD5。

+ * + * @see PasswordEncoder + * @deprecated 此类仅用于遗留系统兼容,新系统应使用更安全的密码编码器 + */ +@Deprecated +public class MD5PasswordEncoder implements PasswordEncoder { + + @Override + public String encode(CharSequence rawPassword) { + if (rawPassword == null) { + throw new IllegalArgumentException("原始密码不能为null"); + } + + byte[] md5Digest = DigestUtils.md5Digest(rawPassword.toString().getBytes()); + return HexFormat.of().formatHex(md5Digest); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + if (rawPassword == null) { + throw new IllegalArgumentException("原始密码不能为null"); + } + + if (!StringUtils.hasText(encodedPassword)) { + return false; + } + + return encodedPassword.equalsIgnoreCase(encode(rawPassword)); + } + + @Override + public boolean upgradeEncoding(String encodedPassword) { + // MD5已不安全,始终返回true建议升级到更安全的算法 + return true; + } +} \ No newline at end of file diff --git a/spring-security/step-1/src/main/resources/application.yml b/spring-security/step-1/src/main/resources/application.yml index 0bee250..8b65d6d 100644 --- a/spring-security/step-1/src/main/resources/application.yml +++ b/spring-security/step-1/src/main/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 8778 + port: 8771 spring: application: diff --git a/spring-security/step-1/src/main/resources/static/favicon.ico b/spring-security/step-1/src/main/resources/static/favicon.ico index 17f9dd1..385f8a6 100644 Binary files a/spring-security/step-1/src/main/resources/static/favicon.ico and b/spring-security/step-1/src/main/resources/static/favicon.ico differ diff --git a/spring-security/step-1/src/main/resources/templates/login.html b/spring-security/step-1/src/main/resources/templates/login.html new file mode 100644 index 0000000..f8b64b3 --- /dev/null +++ b/spring-security/step-1/src/main/resources/templates/login.html @@ -0,0 +1,206 @@ + + + + + + 登录 | 您的应用名称 + + + + + + + + + + +
+ +
+ + + + \ No newline at end of file diff --git a/spring-security/official/src/test/java/com/spring/SpringSecurityApplicationTests.java b/spring-security/step-1/src/test/java/com/spring/SpringSecurityStep1ApplicationTests.java similarity index 80% rename from spring-security/official/src/test/java/com/spring/SpringSecurityApplicationTests.java rename to spring-security/step-1/src/test/java/com/spring/SpringSecurityStep1ApplicationTests.java index 1463f61..9b2aa9c 100644 --- a/spring-security/official/src/test/java/com/spring/SpringSecurityApplicationTests.java +++ b/spring-security/step-1/src/test/java/com/spring/SpringSecurityStep1ApplicationTests.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest -class SpringSecurityApplicationTests { +class SpringSecurityStep1ApplicationTests { @Test void contextLoads() {