1.配置访问安全规则
- 创建WebSecurityConfigurerAdapter 继承类,并注入
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter - 创建ResourceServerConfigurerAdapter继承类,并注入
org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter - 两者的对比总结
WebSecurityConfigurerAdapter 和 ResourceServerConfigurerAdapter 是 Spring Security 中用于配置安全规则的两个核心类,但它们的应用场景和功能有本质区别。以下是两者的对比总结
- 新版本中的替代方案
从 Spring Security 5.x 开始,两者均被弃用,推荐使用 SecurityFilterChain 进行配置:
(1) 替代 WebSecurityConfigurerAdapter
@Configuration
public class WebSecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/login", "/css/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/home")
);
return http.build();
}
}
(2) 替代 ResourceServerConfigurerAdapter
@Configuration
public class ResourceServerConfig {
@Bean
public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.decoder(jwtDecoder())) // 使用 JWT 验证
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri("http://auth-server/.well-known/jwks.json").build();
}
}
- 如何选择?
2.用户信息处理
包括用户名密码、用户角色权限等
- 实现UserDetailsService接口,注入类UserDetailsService
实现用户信息的查找方法,根据系统业务进行封装对象,主要包括:用户名、密码、权限集合等 - 新增和修改用户信息业务实现
2.1 实现密码编码类
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
2.2 用户信息编辑的时候,密码先进行加密,然后保存
sysUser.setPassword(ENCODER.encode(userDto.getNewPassword()));
this.baseMapper.insert(sysUser);
- 用户登录校验
3.1 登录uri
String OAUTH_TOKEN_URL = “/oauth/token”;
3.2 校验前,前端输入的密码处理
一般前端会对密码加密处理,这里要先对应解密
decrypt(String pwd, String passKey)
3.3 创建AuthorizationServerConfigurerAdapter 继承类,并注入
org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter
a.自定义token返回对象属性,例如,用户详细信息
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
final Map<String, Object> additionalInfo = new HashMap<>(4);
additionalInfo.put(SecurityConstants.DETAILS_LICENSE, SecurityConstants.PROJECT_LICENSE);
String clientId = authentication.getOAuth2Request().getClientId();
additionalInfo.put(SecurityConstants.CLIENT_ID, clientId);
// 客户端模式不返回具体用户信息
if (SecurityConstants.CLIENT_CREDENTIALS.equals(authentication.getOAuth2Request().getGrantType())) {
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
UserDetails user = (UserDetails) authentication.getUserAuthentication().getPrincipal();
additionalInfo.put(SecurityConstants.DETAILS_USER, user);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
};
}
b.可以将token放入缓存
private final RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix( "sys_oauth:access:");
return tokenStore;
}
3.4 添加UserDetailsService类引用
3.5 这样,在登录的时候,就会从数据库里查询数据,进行用户信息密码校验,校验成功,就会返回token,供业务调用处理
3.6 校验失败,可以针对异常进行自定义返回的数据格式,实现接口org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator
- 获取当前登录用户信息
public static ActualUser getCurrentUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
return principal instanceof ActualUser ? (ActualUser) principal : null;);
}
public static List<String> getRoles() {
Authentication authentication = getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> roleIds = new ArrayList();
authorities.stream().filter((granted) -> {
return StrUtil.startWith(granted.getAuthority(), "ROLE_");
}).forEach((granted) -> {
String id = StrUtil.removePrefix(granted.getAuthority(), "ROLE_");
roleIds.add(id);
});
return roleIds;
}