自定義feign配置與服務(wù)調(diào)用的安全驗(yàn)證

feign的使用,可以簡化服務(wù)之間的調(diào)用,讓服務(wù)之間調(diào)用更加優(yōu)雅,本文從feign自定義配置和創(chuàng)建feign完成服務(wù)之間復(fù)雜權(quán)限驗(yàn)證,來進(jìn)一步理解和定制feign。

本文示例參考了《Spring Cloud與Docker微服務(wù)架構(gòu)實(shí)踐》

自定義配置

  • 創(chuàng)建Feign的配置類
@Configuration
public class FeignConfiguration{
     @Bean
     public Contract feignContract(){
         return new feign.Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
         return new BasicAuthRequestInterceptor("user","password");
   }
}

說明:第一個(gè)@Bean配置是在FeignClient中的接口中使用Feign自帶的注解; 第二個(gè)@Bean配置是在請求接口中要進(jìn)行基于Http Basic的認(rèn)證后才能調(diào)用。

  • FeignClient接口引用配置
@FeignClient(name="hello", configuration=FeignConfiguration.class)
public interface HelloService{
    @RequestLine("GET /message")
    HelloMessage hello();
}

需要注意的是: FeignConfiguration類不能包含在主應(yīng)用程序上下文的@ComponentScan中,否則該配置會(huì)被所有的@FeignClient共享。

Ribbon的自定義配置中也需要注意這個(gè)問題。

服務(wù)調(diào)用的復(fù)雜權(quán)限認(rèn)證

上面演示了服務(wù)之間可以通過自定義配置完成基于Http Basic的認(rèn)證,但是不能滿足根據(jù)不同的角色不同的用戶執(zhí)行不同的操作,即服務(wù)之間的調(diào)用有更復(fù)雜的權(quán)限要求,就不能滿足要求了,這時(shí)候要針對feign做進(jìn)一步的改進(jìn)。

代碼示例見:hello-auth + hello-auth-consumer-feign的項(xiàng)目
以下是操作步驟:

  • 引入spring-security(服務(wù)提供者)
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
  • 關(guān)鍵權(quán)限配置(服務(wù)提供者)
package com.example.helloauth.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;

/**
 * @author billjiang 475572229@qq.com
 * @create 17-8-26
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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


    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Autowired
    private CustomUserDetailService userDetailService;

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

    @Component
    class CustomUserDetailService implements UserDetailsService{
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
            if("user".equals(username)){
                return new SecurityUser("user","123456","user-role");
            }else if("admin".equals(username)){
                return new SecurityUser("admin","123456","admin-role");
            }else{
                return null;
            }
        }
    }

    class SecurityUser implements UserDetails{
        private static final long serialVersionUID=1L;

        public SecurityUser(String username,String password,String role){
            super();
            this.username=username;
            this.password=password;
            this.role=role;
        }

        public SecurityUser(){

        }

        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            Collection<GrantedAuthority> authorities=new ArrayList<>();
            SimpleGrantedAuthority authority=new SimpleGrantedAuthority(this.role);
            authorities.add(authority);
            return authorities;
        }


        private Long id;
        private String username;
        private String password;
        private String role;

        public Long getId() {
            return id;
        }

        public void setId(Long id) {
            this.id = id;
        }

        @Override
        public String getUsername() {
            return username;
        }

        @Override
        public boolean isAccountNonExpired() {
            return true;
        }

        @Override
        public boolean isAccountNonLocked() {
            return true;
        }

        @Override
        public boolean isCredentialsNonExpired() {
            return true;
        }

        @Override
        public boolean isEnabled() {
            return true;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getRole() {
            return role;
        }

        public void setRole(String role) {
            this.role = role;
        }
    }

}

啟動(dòng)項(xiàng)目hello-auth后,可以見到頁面需要輸入用戶名和密碼才能訪問。


權(quán)限認(rèn)證.png
  • 服務(wù)消費(fèi)者h(yuǎn)ello-auth-feign修改
    1)去掉啟動(dòng)類上的@EnableFeignClients注解
    2)去掉接口的@FeignClient的注解
package com.example.helloauthconsumerfeign.service;

import com.example.helloauthconsumerfeign.model.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author billjiang 475572229@qq.com
 * @create 17-8-23
 */
//@FeignClient(value="hello-auth")
public interface HelloAuthService {

    @GetMapping("/{id}")
    User findById(@PathVariable("id") Long id);


}

3)編寫Controller類如下

package com.example.helloauthconsumerfeign.controller;

import com.example.helloauthconsumerfeign.model.User;
import com.example.helloauthconsumerfeign.service.HelloAuthService;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import feign.codec.Decoder;
import feign.codec.Encoder;
import org.springframework.cloud.netflix.feign.FeignClientsConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


/**
 * @author billjiang 475572229@qq.com
 * @create 17-8-26
 */
@Import(FeignClientsConfiguration.class)
@RestController
public class HelloAuthFeignController {
    private HelloAuthService userAuthService;

    private HelloAuthService adminAuthService;

    @Autowired
    public HelloAuthFeignController(Decoder decoder, Encoder encoder, Client client, Contract contract){
        this.userAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
                .requestInterceptor(new BasicAuthRequestInterceptor("user","123456"))
                .target(HelloAuthService.class,"http://hello-auth/");

        this.adminAuthService= Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
                .requestInterceptor(new BasicAuthRequestInterceptor("admin","123456"))
                .target(HelloAuthService.class,"http://hello-auth/");
    }

    @GetMapping("/user/{id}")
    public User findByIdUser(@PathVariable Long id){
        return this.userAuthService.findById(id);
    }


    @GetMapping("/admin/{id}")
    public User findByIdAdmin(@PathVariable Long id){
        return this.adminAuthService.findById(id);
    }


}

  • 消費(fèi)者調(diào)用
    在瀏覽器器分別輸入http://localhost:8031/admin/1http://localhost:8031/user/2則可以完成服務(wù)之間授權(quán)的調(diào)用。

在實(shí)際業(yè)務(wù)中會(huì)根據(jù)不同的角色,執(zhí)行不同的業(yè)務(wù)操作,基于以上示例可以在服務(wù)之間完成復(fù)雜的服務(wù)鑒權(quán)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,695評論 19 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,288評論 6 342
  • 國家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說閱讀 12,535評論 6 13
  • 昏黃的燈光 在深夜檢閱旁觀者的懶散 她倚靠在床頭,紉下時(shí)光的長線 再縫一個(gè)沙包吧,填裝秋收的小麥 在操場的一角,一...
    倩何人換取閱讀 192評論 0 2
  • 五年之后,我要開始奔五了。哈哈,當(dāng)年2000年,世紀(jì)之交都覺得那么遙遠(yuǎn);眨眼人生已經(jīng)快過半了。 所以,不能再和過去...
    含香2016閱讀 440評論 1 3

友情鏈接更多精彩內(nèi)容