1、maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
如果需要JDBC的动态数据认证则需要加如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
密码加密依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.82</version>
</dependency>
2、配置文件
spring:
application:
name: serurity
datasource:
url: jdbc:mysql://localhost:3306/security_test
username: root
password: 123abc?
driver-class-name: com.mysql.cj.jdbc.Driver
3、如果使用jdbc数据库用户认证 则需要新建数据表(用户表users和权限表authorities),
用户表字段

权限表字段

4、新建用户管理类UserManagementConfig
package com.example.serurity.config;
import com.example.serurity.test.SimpleUser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.*;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import javax.sql.DataSource;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class UserManagementConfig {
//用户详情服务
// @Bean
// public UserDetailsService userDetailsService() {
//
// InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
//
// UserDetails user= User.withUsername("john")
// .password("12345")
// .authorities("read").build();
//
//
// userDetailsService.createUser(user);
//
// return userDetailsService;
// }
// @Bean
// public UserDetailsService userDetailsService() {
// //自定义练习
// UserDetails userDetails=new SimpleUser("john","127345","read");
// return new InMemoryUserDetailsService(List.of(userDetails));
// }
@Bean
public UserDetailsService userDetailsService(DataSource dataSource) {
String usersByUsernameQuery = "select username,password,enabled from users where enabled=1 and username = ?";
String authsByUserQuery="select username,authority from authorities where username = ?";
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager(dataSource);
jdbcUserDetailsManager.setUsersByUsernameQuery(usersByUsernameQuery);
jdbcUserDetailsManager.setAuthoritiesByUsernameQuery(authsByUserQuery);
return jdbcUserDetailsManager;
}
//密码编码
@Bean
public PasswordEncoder passwordEncoder() throws NoSuchAlgorithmException {
//方式1
// return Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
//方式2
// BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(4);
// BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(4, SecureRandom.getInstanceStrong());
// return bCryptPasswordEncoder;
//方式3
// SCryptPasswordEncoder sCryptPasswordEncoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
// return sCryptPasswordEncoder;
//方式4
Map<String, PasswordEncoder> encoderMap=new LinkedHashMap<>();
encoderMap.put("bcrypt", new BCryptPasswordEncoder());
encoderMap.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
encoderMap.put("noop",NoOpPasswordEncoder.getInstance());
return new DelegatingPasswordEncoder("scrypt",encoderMap);
}
}
5、新建身份验证提供程序类CustomAuthenticationProvider
package com.example.serurity.service_config;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
// AuthenticationManager
@Resource
@Lazy
private UserDetailsService userDetailsService;
@Resource
@Lazy
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails user = userDetailsService.loadUserByUsername(username);
if(passwordEncoder.matches(password,user.getPassword())){
return new UsernamePasswordAuthenticationToken(username,password,user.getAuthorities());
}else {
throw new BadCredentialsException("密码不正确");
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
6、新建跨域配置类CorsConfig
package com.example.serurity.config;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import java.util.List;
public class CorsConfig implements CorsConfigurationSource {
//全局跨域配置
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:4200"));
//config.addAllowedOrigin("*");
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
return config;
}
}
7、新建Basic验证失败执行类CustomEntryPoint
package com.example.serurity.handler;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
public class CustomEntryPoint implements AuthenticationEntryPoint {
//身份验证失败时,执行
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.addHeader("message","I am you father");
response.sendError(HttpStatus.UNAUTHORIZED.value());
}
}
9、新建身份验证类,整合所有的配置类WebAuthenticationConfig
package com.example.serurity.config;
import com.example.serurity.filter.AuthenticationLoggingFilter;
import com.example.serurity.filter.CsrfTokenLogger;
import com.example.serurity.filter.RequestValidationFilter;
import com.example.serurity.handler.CustomCsrfTokenRepository;
import com.example.serurity.handler.CustomEntryPoint;
import com.example.serurity.service_config.CustomAuthenticationProvider;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import java.util.List;
@Configuration
@EnableWebSecurity
public class WebAuthenticationConfig {
@Resource
@Lazy
private CustomAuthenticationProvider customAuthenticationProvider;
//授权
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
//access的自定义表达式
// AuthorizationManager<RequestAuthorizationContext> authorizationManager=new WebExpressionAuthorizationManager("""
// hasRole("ADMIN") && !hasRole("MANAGER")
// """);
//配置允许12点之后进行访问
AuthorizationManager<RequestAuthorizationContext> authorizationManager=new WebExpressionAuthorizationManager("""
T(java.time.LocalTime).now().isAfter(T(java.time.LocalTime).of(12,0))
""");
//正则表达式
RequestMatcher regexRequestMatcher = new RegexRequestMatcher("^/api/v1/.*",null);
// http.addFilterBefore(new RequestValidationFilter(), BasicAuthenticationFilter.class)
// .addFilterAfter(new AuthenticationLoggingFilter(), BasicAuthenticationFilter.class);
// http.addFilterAfter(new CsrfTokenLogger(), CsrfFilter.class);
http.httpBasic(c->{
c.realmName("OTHER");
c.authenticationEntryPoint(new CustomEntryPoint());
}).authorizeHttpRequests(auth -> auth
.requestMatchers("/hello").hasRole("ADMIN")
.anyRequest().authenticated()
// .requestMatchers(HttpMethod.POST,"/hello/*").hasRole("ADMIN")
// .requestMatchers(regexRequestMatcher).hasRole("ADMIN")
).authenticationProvider(customAuthenticationProvider);
http.csrf(v->{
v.csrfTokenRepository(new CustomCsrfTokenRepository());
v.ignoringRequestMatchers("/helloPost");
});
//跨域
http.cors(c->{
c.configurationSource(new CorsConfig());
});
return http.build();
}
}