SpringCloud微服务之四:Feign实现声明式REST调用(二)
文章目录
主要学习SpringCloud的手动创建Feign,面对更复杂的业务环境,将Feign与springboot中的spring-security认证鉴权相结合;以及Feign对压缩的支持;Feign的日志配置等。
一、服务提供者处理
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.添加配置类
package com.yangxf.cloud.study.security;
import java.util.ArrayList;
import java.util.Collection;
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;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//所有方法调用前进行验证
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 所有的请求,都需要经过HTTP basic认证
http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.userDetailsService).passwordEncoder(this.passwordEncoder());
}
@Component
class CustomUserDetailsService implements UserDetailsService {
/**
* 模拟两个账户,内存存储账户:
* ① 账号是user,密码是password1,角色是user-role
* ② 账号是admin,密码是password2,角色是admin-role
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//也可以根据从数据库获取账号信息,进行用户信息认证
if ("user".equals(username)) {
return new SecurityUser("user", "password1", "user-role");
} else if ("admin".equals(username)) {
return new SecurityUser("admin", "password2", "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() {
}
private Long id;
private String username;
private String password;
private String role;
//返回用户所拥有的权限
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role);
authorities.add(authority);
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return this.role;
}
public void setRole(String role) {
this.role = role;
}
}
}
3、添加对外的控制器
package com.yangxf.cloud.study.controller;
import java.util.Collection;
import com.yangxf.cloud.study.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.yangxf.cloud.study.entity.User;
import com.yangxf.cloud.study.repository.UserRepository;
@RestController
public class UserController {
@Autowired
private UserRepository userRepository;
private static final Logger LOGGER = LoggerFactory.getLogger(UserController.class);
@GetMapping("/{id}")
public User findById(@PathVariable Long id) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
UserDetails user = (UserDetails) principal;
Collection<? extends GrantedAuthority> collection = user.getAuthorities();
for (GrantedAuthority c : collection) {
// 打印当前登录用户的信息
UserController.LOGGER.info("当前用户是{},角色是{}", user.getUsername(), c.getAuthority());
}
} else {
// do other things
}
User findOne = this.userRepository.findOne(id);
return findOne;
}
}
三、服务消费者添加相关
1、调用提供者服务的配置类
public interface UserFeignClient {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public User findById(@PathVariable("id") Long id);
}
2、控制器
package com.yangxf.cloud.study.user.controller;
import com.yangxf.cloud.study.user.entity.User;
import com.yangxf.cloud.study.user.feign.UserFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
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;
import com.yangxf.cloud.study.user.entity.User;
import com.yangxf.cloud.study.user.feign.UserFeignClient;
import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
@Import(FeignClientsConfiguration.class)//Feign提供的默认的配置类,导入配置类
@RestController
public class MovieController {
private UserFeignClient userUserFeignClient;
private UserFeignClient adminUserFeignClient;
@Autowired
public MovieController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
// 这边的decoder、encoder、client、contract,可以debug看看是什么实例。
this.userUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("user", "password1")).target(UserFeignClient.class, "http://microservice-provider-user/");
this.adminUserFeignClient = Feign.builder().client(client).encoder(encoder).decoder(decoder).contract(contract)
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "password2"))
.target(UserFeignClient.class, "http://microservice-provider-user/");
}
@GetMapping("/user-user/{id}")
public User findByIdUser(@PathVariable Long id) {
return this.userUserFeignClient.findById(id);
}
@GetMapping("/user-admin/{id}")
public User findByIdAdmin(@PathVariable Long id) {
return this.adminUserFeignClient.findById(id);
}
}
四、Feign对压缩的支持
一些场景下,可能需要对请求或者响应进行压缩,Feign是支持该功能的。
具体配置是否启用如下,可以自定义配置压缩的支持类型以及设置最小阀门,超过限定阀门就会进行压缩。
feign:
compression:
request:
enabled: true
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
response:
enabled: true
五、Feign的日志等级配置
feign:
client:
config:
default: #默认全局配置
loggerLevel: basic
oneFeignName: #feign服务名
loggerLevel: full #配置日志级别,相当于代码配置中Logger
也可以通过java配置类添加:
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.full;
}
none:不记录任何日志(默认值);
basic:仅记录请求方法、url、响应状态代码以及执行时间;
headers:记录basic级别的基础上,记录请求和响应的header;
full:记录请求和响应的headr、body和元数据。