Spring Security OAuth2入门踩坑

吐槽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

1.png

通过OAuth2获取token http://127.0.0.1:8080/oauth/token?username=user1&password=123456&grant_type=password&client_id=client1&client_secret=123456

2.png

通过获取的token访问资源

3.png

介绍Spring Security原理

通过DelegatingFilterProxy、FilterChainProxy、SecurityFilterChain实现认证和授权,需要特别注意的是1.一次请求里只有最先匹配到的SecurityFilterChain会被执行。2.SecurityFilterChain里有一系列跟认证/授权相关的过滤器,其中包括FilterSecurityInterceptor。

4.png

5.png

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;
}
复制代码

6.png

以下是阅读过的其他作者写的文章

猜你喜欢

转载自juejin.im/post/7032933319620689951