zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    机器学习-线性回归
    机器学习-朴素贝叶斯
    ML-first project
    机器学习-决策树
    当矩阵的秩小于未知数的个数时,方程组有无数个解;当矩阵的秩等于未知数的个数时,方程组只有零解。
    并发编程futuretask
    Java重头学
    InputString 转换成 BufferedImage 和 byte[]
    mysql批量更新
    关于ajax请求数据,并将数据赋值给全局变量的一些解决方法
  • 原文地址:https://www.cnblogs.com/helloz/p/11079098.html
Copyright © 2011-2022 走看看