1. 程式人生 > >Spring boot+Security OAuth2 爬坑日記(1)授權碼模式

Spring boot+Security OAuth2 爬坑日記(1)授權碼模式

OAuth2

OAuth 關於授權的開放網路標準,關於OAuth2的知識,參考OAuth2.0理解OAuth 2.0-阮一峰

四種授權模式

  1. 授權碼模式 (功能最完整,流程最嚴密)
  2. 密碼模式
  3. 客戶端模式
  4. 簡化模式

授權碼模式

圖片來自 https://tools.ietf.org/html/rfc6749#section-4.1

開發環境

系統:Mac / Win10 兩個開發環境交替使用

JDK:java11(Mac),java8(Win10)

IDEA:Intellij IDEA (2018.1-win),Intellij IDEA (2018.3-mac)

專案管理: maven 3.5

maven 依賴

<
parent
>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</
groupId
>
<artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId
>
org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.11</version> </dependency> </dependencies>

認證服務配置

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    
    @Autowired
    private PasswordEncoder passwordEncoder;
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
       // 允許表單登入
       security.allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        String secret = passwordEncoder.encode("123qwe");

        clients
        		// 客戶端資訊儲存在記憶體中
                .inMemory()
                // 客戶端 id
                .withClient("client")
                // 跳轉uri
                .redirectUris("http://localhost")
                // 客戶端 secret 
                .secret(secret)
                // 授權模式
                .authorizedGrantTypes("refresh_token","authorization_code");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        		// tonken 儲存於記憶體中
                .tokenStore(new InMemoryTokenStore()) 
                .authenticationManager(authenticationManager);
    }
}

資源服務配置

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig  extends ResourceServerConfigurerAdapter{

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        super.configure(resources);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
       http.formLogin().and()
                .requestMatchers().anyRequest()
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/**","/login").permitAll()
                .anyRequest().authenticated();
    }
}

WebSecurity 配置

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private BootUserDetailService userDetailService;
    @Autowired
    private BootLoginSuccessHandler bootLoginSuccessHandler;

    /**
     * 讓Security 忽略這些url,不做攔截處理
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers
                ("/swagger-ui.html/**", "/webjars/**",
                        "/swagger-resources/**", "/v2/api-docs/**",
                        "/swagger-resources/configuration/ui/**", "/swagger-resources/configuration/security/**",
                        "/images/**");
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }


    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


}

自定義 UserDetailsService

@Component
public class BootUserDetailService implements UserDetailsService {

    @Autowired
    private IUserService userService;

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user= this.userService.findByUserName(username);

        GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_USER");

        List <GrantedAuthority>authorities = new ArrayList<>();
        authorities.add(authority);
        user.setAuthorities(authorities);

        if(user==null) {
            throw new UsernameNotFoundException("使用者名稱不存在");
        }

        return user;
    }
}

啟動系統後在控制檯中打印出如下的請求介面


/oauth/authorize   使用者確認授權路徑

/oauth/token       獲取token的路徑

參考OAuth2.0 官網的文件,請求授權的路徑為

GET http://localhost:8000/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://localhost&scope=select

訪問該URL 登入後會出現如下的請求授權的頁面
是否授權

點選(Authorize)確認授權,之後會跳轉到預先在請求授權URL中設定的(redirect_uri)的引數值指定的頁面並帶上授權碼,跳轉後出現如下頁面
在這裡插入圖片描述

得到授權碼之後,請求獲取Token ,請求引數如下
在這裡插入圖片描述

到這裡就獲取到了Token,接下來就可以帶上Token去請求相關的資源
在這裡插入圖片描述

如果不帶上Token,將會有如下錯誤
在這裡插入圖片描述

到這裡簡單的OAuth2的授權碼模式完成,接下來總結下在完成這個接單示例的過程中遇到的坑:

  1. 在授權之前必須完成登入,所以WebSecurityConfig必須必須在ResourceServerConfig之前,如果不是的話就會出現如下異常
User must be authenticated with Spring Security before authorization can be completed.

原始碼地址 Github
微信公眾號:
在這裡插入圖片描述