• Spring security oauth2 password flow


    Spring security oauth2 包含以下两个endpoint来实现Authorization Server:

    AuthorizationEndpoint: 授权请求访问端点, 默认url: /oauth/authorize

    TokenEndpoint: 获取access token的请求的访问端点, 默认url: /oauth/token

    添加依赖

    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
    </dependency>
    

    配置Authorization Server

    @EnableAuthprizationServer注解, 配合实现了AuthorizationServerConfigurer的配置类来配置Authorization Server。

    AuthorizationServerConfigurer中主要的配置有三个, 覆盖对应的方法即可:

    ClientDetailsServiceConfigurer:

    定义了client detail service, 可以简单的指定几个client, 或者指向数据库中的表。

    可以存储client信息到内存中或数据库中, 即in-memory和JDBC两种实现。

    AuthorizationServerSecurityConfigurer:

    定义了token端点的安全策略

    其中有两个重要的端点:

    /oauth/check_token: 检查token的有效性

    /oauth/token_key: 获取token的key

    AuthorizationServerEndpointsConfigurer:

    定义authorization endpoint, token endpoint以及token service。token service管理token相关的一切, 除了token的持久化是委托给TokenStore来实现, 默认的实现是DefaultTokenServices.

    有三种类型的TokenStore:

    InMemoryTokenStore: 默认实现, 存储在内存中。

    JdbcTokenStore: token数据存储在关系型数据库中。

    JWT 类型的TokenStore, 典型的是JwtTokenStore, 不存储到任何地方, 把所有的数据编码到token中, 因此token的撤回(revoke)实现起来优点麻烦。

    配置:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.ResponseEntity;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
    import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
    import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    
    import java.util.Arrays;
    
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
      @Value("${security.oauth2.client.client-id}")
      private String clientId;
    
      @Value("${security.oauth2.client.client-secret}")
      private String clientCredentials;
    
      private final PasswordEncoder passwordEncoder;
    
      private final AuthenticationManager authenticationManager;
    
      private final DefaultTokenEnhancer defaultTokenEnhancer;
      
      private final UserDetailsService userDetailsService;
    
      @Autowired
      public AuthorizationServerConfig(PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager,
                                       DefaultTokenEnhancer defaultTokenEnhancer,
                                       UserDetailsService userDetailsService) {
        this.passwordEncoder = passwordEncoder;
        this.authenticationManager = authenticationManager;
        this.defaultTokenEnhancer = defaultTokenEnhancer;
        this.userDetailsService = userDetailsService;
      }
    
      public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
        configurer
            .inMemory()
            .withClient(clientId)
            .secret(passwordEncoder.encode(clientCredentials))
            .authorizedGrantTypes("password", "refresh_token")
            .accessTokenValiditySeconds(3600);
      }
    
      @Override
      public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer.tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .allowFormAuthenticationForClients(); // 默认通过basic authentication, 即加一个header为Authorization: Basic base64(username:password), 指定这个可以设置form提交, 把client-id, client-secret放到post参数中。
      }
    
      @Bean
      TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
      }
    
      @Bean
      public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtConverter = new JwtAccessTokenConverter();
        jwtConverter.setSigningKey("your-sign-key");
        return jwtConverter;
      }
    
      @Bean
      public WebResponseExceptionTranslator loggingExceptionTranslator() {
        return new DefaultWebResponseExceptionTranslator() {
          @Override
          public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
            e.printStackTrace();
            ResponseEntity<OAuth2Exception> responseEntity = super.translate(e);
            HttpHeaders headers = new HttpHeaders();
            headers.setAll(responseEntity.getHeaders().toSingleValueMap());
            OAuth2Exception excBody = responseEntity.getBody();
            return new ResponseEntity<>(excBody, headers, responseEntity.getStatusCode());
          }
        };
      }
    
      public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(defaultTokenEnhancer, jwtAccessTokenConverter()));
    
        endpoints.authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService)
        .tokenStore(tokenStore())
        .tokenEnhancer(tokenEnhancerChain)
        .exceptionTranslator(loggingExceptionTranslator());
      }
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    @Configuration
    @EnableWebSecurity
    @Order(1)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
      @Bean
      public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(10);
      }
    
      @Bean
      public AuthenticationManager authenticationManagerBean() throws Exception {
        return this.authenticationManager();
      }
    
      protected void configure(HttpSecurity http) throws Exception {
        http
            .formLogin().permitAll()
            .and()
            .csrf().disable()
            .cors().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS).permitAll()
            .antMatchers("/oauth/**").permitAll()
            .anyRequest().authenticated();
      }
    }
    

    formLogin().permitAll()对password flow不是必须的, 只对implicit flow有影响。

    当时测试implicit flow的时候确实是这样, 在访问/oauth/authorize之后, 需要登录, 登录后一直提示错误, 但没有错误信息, 从而获取不到token, 加了formLogin().permitAll()选项后就正常返回token了。

    获取access token:

    访问地址: POST localhost:8080/oauth/token

    参数:client_id=clientId&client_secret=clientSecret&grant_type=password&username=username&password=password

    刷新access token:

    POST localhost:8080/oauth/token

    参数:

    client_id=clientId&client_secret=clientSecret&grant_type=refresh_token&scope=any&refresh_token=refreshToken

    顺便介绍一下其他的grant type:

    grant type授权类型一般有client credentials, password, implicit, authorization code, refresh。

    client credentials是直接访问/oauth/token端点带grant_type

    password 访问/oauth/token端点带grant_type

    implicit 访问/oauth/authorize端点带response_type参数, 值为token, 然后会直接重定向到你指定的redirect_uri, url中带access_token参数

    authorize code 先访问/oauth/authorize端点带response_type参数, 值为code, 然后它会返回一个code参数; 带上code参数访问/oauth/token端点获取access token。

    refer:

    https://www.baeldung.com/rest-api-spring-oauth2-angular

    https://dzone.com/articles/spring-boot-oauth2-getting-the-authorization-code

    https://walkingtree.tech/securing-microservices-oauth2/

    https://projects.spring.io/spring-security-oauth/docs/oauth2.html

  • 相关阅读:
    hdu 3790 最短路径问题
    hdu 2112 HDU Today
    最短路问题 以hdu1874为例
    hdu 1690 Bus System Floyd
    hdu 2066 一个人的旅行
    hdu 2680 Choose the best route
    hdu 1596 find the safest road
    hdu 1869 六度分离
    hdu 3339 In Action
    序列化和反序列化
  • 原文地址:https://www.cnblogs.com/helloz/p/11079098.html
Copyright © 2020-2023  润新知