吐槽Spring Security
刚开始使用的时候确实很简单,只需要添加依赖和几个配置类就行,但到后面只是想OAuth2和Spring Security默认登录功能一起用的时候,发现无从下手。看来不对Spring Security有个大概的了解,那么灵活是不存在的。
最简的配置
修改pom.xml
<!-- 若使用Spring Security则需要配置该依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 若使用OAuth2则还需额外配置该依赖 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
复制代码
新增Spring Security配置类
@EnableWebSecurity
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public UserDetailsService userDetailsServiceBean() {
UserDetails user = User
// 这里配置个用户名是user1密码是123456的用户
.withUsername("user1")
.password(passwordEncoder().encode("123456"))
// 这里的USER是自定义,没有什么特殊的含义
.roles("USER")
.build();
// InMemoryUserDetailsManager设计目的主要是测试和功能演示
return new InMemoryUserDetailsManager(user);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
复制代码
新增OAuth2配置类(注意Spring Security过滤器会因为@Order顺序而在执行的时候有先后顺序,默认情况下过滤器顺序是OAuth2 authorization -> OAuth2 resource -> Spring security)
@EnableAuthorizationServer
@Configuration
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final PasswordEncoder passwordEncoder;
public OAuth2AuthorizationConfig(AuthenticationManager authenticationManager,
PasswordEncoder passwordEncoder) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// allowFormAuthenticationForClients只能放到tokenKeyAccess
// 和checkTokenAccess后面,否则调不通OAuth2。
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 这里配置个客户ID是client1密码是123456的客户端
clients.inMemory()
.withClient("client1")
.secret(passwordEncoder.encode("123456"))
.authorizedGrantTypes("authorization_code", "password", "refresh_token")
// 这里的ALL是自定义,没有什么特殊的含义
.scopes("ALL")
.accessTokenValiditySeconds(3600);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
复制代码
@EnableResourceServer
@Configuration
public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// !!!这里我配置成只处理请求头里有"Authorization"属性的请求
http.requestMatcher((request) -> {
return request.getHeader("Authorization") != null;
});
http.authorizeRequests()
.anyRequest()
.authenticated();
}
}
复制代码
通过Spring Security自带的登录页面登录http://127.0.0.1:8080/login
通过OAuth2获取token http://127.0.0.1:8080/oauth/token?username=user1&password=123456&grant_type=password&client_id=client1&client_secret=123456
通过获取的token访问资源
介绍Spring Security原理
通过DelegatingFilterProxy、FilterChainProxy、SecurityFilterChain实现认证和授权,需要特别注意的是1.一次请求里只有最先匹配到的SecurityFilterChain会被执行。2.SecurityFilterChain里有一系列跟认证/授权相关的过滤器,其中包括FilterSecurityInterceptor。
Spring Security主要组成部分(复制www.springcloud.cc/spring-secu…)
SecurityContextHolder
, 提供几种访问SecurityContext
的方式。SecurityContext
, 保存Authentication
信息和请求对应的安全信息。Authentication
, 展示Spring Security特定的主体。GrantedAuthority
, 反应,在应用程序范围你,赋予主体的权限。UserDetails
,通过你的应用DAO,提供必要的信息,构建Authentication对象。UserDetailsService
, 创建一个UserDetails
,传递一个String
类型的用户名(或者证书ID或其他).
SecurityContextHolder是最根本的对象,通过它可以获取安全上下文SecurityContext。
// 代码来自https://www.springcloud.cc/spring-security-zhcn.html
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
复制代码
UserDetailsService用于根据用户名加载用户信息。
public interface UserDetailsService {
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
复制代码
ProviderManager是AuthenticationManager的实现,通过一系列AuthenticationProvider实现类验证认证信息。
public interface AuthenticationManager {
Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
复制代码
以下是阅读过的其他作者写的文章
- Spring Security中文参考手册 www.springcloud.cc/spring-secu…
- blog.csdn.net/weixin_4387…