網上有不少與此類型類似的文章,可是在我搭建網關集成認證功能時,要麼參考的這些文章並不全,要麼不是我想要的或者說是比較複雜。所以本篇將會搭建一個簡易的網關服務,採用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(好像換人維護了)