前言
有这么个需求:客户端不能直接访问后端服务,需经过网关进行权鉴及安全认证后在将请求转发到后端。但如果开发人员过用户知道后端地址这个时候完全可以跳过网关服务直接请求到后端,这样后端服务就会承受安全风险,这个时候我们就要用到Spring Security。
Spring Security简介
Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
SpringBoot集成Security
这里我们将Security单独建立为一个核心微服务:microservice-security,这样其他后端微服务执行引入这个jar就可以实现安全认证。
pom我们导入如下jar:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-data-couchbase
同时我们编写WebSecurityConfig类,该类继承WebSecurityConfigurerAdapter,现通过以下方法可配置拦截URL,配置忽略认证地址及设置什么权限等安全控制。
@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override public void configure(WebSecurity web) { String[] antPatterns = new String[] { /** 忽略异步推送地址权鉴 */ "/v1/notifyUrl", "/v1/faceLite/gainResultAndReturnUrl" }; /**忽略auth认证URL*/ web.ignoring().antMatchers(antPatterns); } @Override protected void configure(HttpSecurity http) throws Exception { /**表示所有的访问都必须进行认证处理后才可以正常进行*/ http.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated(); /**所有的Rest服务一定要设置为无状态,以提升操作性能*/ http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.csrf().disable(); }}
配置application-security.yml,设置安全认证密码:
spring: security: user: name: wxt12138 password: wxt12138 roles: - USER - ACTUATOR
通过上述三步我们编写提供端微服务并引入microservice-security,注意提供端微服务application.yml配置需要引入security的配置文件:
spring: profiles: include: - security
此时我们直接访问提供端服务会出现401异常。
{ "timestamp": "2019-12-03T16:46:59.961+0000", "status": 401, "error": "Unauthorized", "message": "Unauthorized", "path": "/xxx/xxx"}
此时我们的安全认证是已经生效,这时我们还需要配置gateway网关服务,网关服务中实现OAuth授权。相关代码片段如下:
@Componentpublic class OAuthSignatureFilter implements GlobalFilter, Ordered { /**授权访问用户名*/ @Value("${spring.security.user.name}") private String securityUserName; /**授权访问密码*/ @Value("${spring.security.user.password}") private String securityUserPassword; /**OAuth过滤器*/ @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { /**oauth授权*/ String auth = securityUserName.concat(":").concat(securityUserPassword); String encodedAuth = null; try { encodedAuth = Base64Utils.encode(auth.getBytes(Charset.forName("US-ASCII"))); } catch (UnsupportedEncodingException e) { MySlf4j.textError("BASE64编码异常:{0}",MySlf4j.ExceptionToString(e)); } String authHeader = "Basic " + encodedAuth; //向headers中放授权信息 ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().header("Authorization", authHeader) .build(); //将现在的request变成change对象 ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build(); return chain.filter(build); } /**优先级,数字越大优先级越低 */ @Override public int getOrder() { return 3; }}
至此我们完成了SpringBoot服务的权限认证。