Spring Cloud 基於OAth2協議與JWT搭建一個簡易網關

    網上有不少與此類型類似的文章,可是在我搭建網關集成認證功能時,要麼參考的這些文章並不全,要麼不是我想要的或者說是比較複雜。所以本篇將會搭建一個簡易的網關服務,採用oauth2協議的passowrd模式來進行身份認證,jwt做爲token令牌。最終達到須要登陸獲取AccessToken才能向網關申請服務資源的效果。本篇博文代碼已託管至碼雲。java

    項目總體的目錄結構以下git

    版本:Spring Cloud Edgware.SR3spring

              Spring Boot 1.5.12.RELEASEapi

    1.首先建立父工程安全

    pom.xml部分以下服務器

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring.cloud.version>Edgware.SR3</spring.cloud.version>
        <spring.boot.version>1.5.12.RELEASE</spring.boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    2.搭建Eureka服務治理session

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

    3.搭建一個示例資源服務app

    提供/test端點用於測試less

@RestController
@EnableEurekaClient
@SpringBootApplication
public class ResourceServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ResourceServiceApplication.class, args);
    }

    @GetMapping("/test")
    public String testAuthentication() {
        return "authentication permit";
    }
}

    4.搭建認證服務ide

    用到以下依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

    認證服務配置

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //使用內存存儲
        clients.inMemory()
                //分配客戶端帳號
                .withClient("client")
                .secret("client-secret")
                //支持的受權類型
                .authorizedGrantTypes("refresh_token", "password")
                .scopes("server")
                //token有效時長
                .accessTokenValiditySeconds(1200)
                //refreshToken有效時長
                .refreshTokenValiditySeconds(50000);
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //設置簽名密鑰
        jwtAccessTokenConverter.setSigningKey("demo");
        return jwtAccessTokenConverter;
    }

    //使用JWT做爲token
    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(jwtTokenStore())
                .accessTokenConverter(jwtAccessTokenConverter())
                .reuseRefreshTokens(true)
                //配置以生效password模式
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients()
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()");
    }
}

    注意:這裏須要配置authenticationManager不然沒法支持password模式。當獲取token時,出現以下響應。

{
        "error": "unsupported_grant_type",
        "error_description": "Unsupported grant type: password"
}

    安全服務配置

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();
    }

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

    簡單模擬用戶校驗服務

@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private PasswordEncoder passwordEncoder;

    //username:任意 password:123456
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, passwordEncoder.encode("123456"), 
                        AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

    application.yml

spring:
  application:
    name: auth-server
server:
  port: 4040
security:
  oauth2:
    resource:
      filter-order: 3
management:
  security:
    enabled: false
eureka:
  client:
    service-url:
      defaultZone: http://eureka:pwd@localhost:8761/eureka

    5.搭建服務網關

    資源服務配置

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                //去掉獲取token會被攔截
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated();
    }
}

    注意:這裏須要放行/auth/**不然網關會在獲取token請求時就對其身份進行校驗攔截

    添加網關過濾器

@Component
public class AuthFilter extends ZuulFilter {
    //攔截類型
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    //攔截順序
    @Override
    public int filterOrder() {
        return SERVLET_DETECTION_FILTER_ORDER - 1;
    }

    //開啓攔截
    @Override
    public boolean shouldFilter() {
        return true;
    }

    //攔截處理邏輯
    @Override
    public Object run() {
        //獲取用戶認證信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(authentication);
        return null;
    }
}

    application.yml

spring:
  application:
    name: api-gateway
server:
  port: 8040
eureka:
  client:
    service-url:
      defaultZone: http://eureka:pwd@localhost:8761/eureka
zuul:
  ignoredServices: '*'
  routes:
    resource-service: /demo/**
    auth-server: /auth/**
  sensitive-headers:

management:
  security:
    enabled: false

security:
  oauth2:
    client:
      //在認證服務器中配置的客戶端帳號
      clientId: client
      clientSecret: client-secret
    resource:
      token-info-uri: http://localhost:4040/oauth/check_token
      prefer-token-info: true
      jwt:
        key-uri: http://localhost:4040/oauth/token_key
  sessions: stateless

    注意:因爲使用token-info-uri,所以需先啓動auth-server再啓動網關服務,由於網關服務在啓動時會調用該端點獲取數據。如先啓動網關會出現以下報錯日誌。

I/O error on GET request for "http://localhost:4040/oauth/token_key": Connection refused: connect;
nested exception is java.net.ConnectException: Connection refused: connect

    至此,一個簡單的網關認證服務就搭建完成了。接下來,讓咱們來測試一下。

    直接經過網關代理請求測試端點,返回結果以下,響應提示須要認證後纔可訪問資源。

    password模式獲取jwt token

    經過獲取到的access_token請求資源服務

    控制檯可見以下輸出,可見在過濾器獲取到了用戶的身份信息

org.springframework.security.oauth2.provider.OAuth2Authentication@407c5f9d: Principal: user;
Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1,
tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER

    本篇博文從PIG項目中簡化剝離出來,若有須要進一步拓展的小夥伴們,推薦兩個開源項目

    冷冷——PIG

    老A——AG-Admin(好像換人維護了)

相關文章
相關標籤/搜索