Spring Cloud Micro security services combat _5-5_refresh_token

Benpian solve a problem, token is valid

 

token is a short-living things, session may be three days, but the token may be two hours, then it will be a case, session also effective but token fails, this time to take this token to call other micro services will fail.

This involves Refresh token OAuth2 protocol, refresh token. Refresh token to say is that whether you use the OAuth protocol in four types of authorization which (password mode, authorization code mode, reduced mode, client mode), when the token fails, you can hold a refresh_token to retrieve a token without the need to enter a user name and password. So that users get a short life cycle access_token, and refresh_token a long life cycle, when access_token failure, took refresh_token exchange for a new access_token.

In theory, access_token can set a very long period, but this is not safe, as long as get the access_token, you can access the service, so if you do, the risk is high. The refresh_token not the same, the following chart can be seen, in order to pass refresh_token exchange access_token, need to carry clientId and clientSecret, the authentication server verifies They, both of which are stored on the server side, other people can not get in, so even if someone else got refresh_token it is useless.

 Refresh token where?

Written in front of a twenty articles, and never not seen refresh_token, this is because the client application table oauth_client_details  which has a field refresh_token_validity, if not deserve this field, the authentication server will not send refresh_token, if equipped with this value, the authentication server will send access_token at the same time, also made a refresh_token.  

 

模拟token失效案发现场

清空掉 access_token表(清掉之前的长有效期的token)

 

 

 将客户端应用表的 admin 应用的token有效期设置为10秒,这样在登录成功后,客户端应用的session和认证服务器的session还未失效的情况下,token就会失效

 

 

重新登录,获取一个有效期10秒的token,调用订单微服务,前几次调用由于token还没过期,调用成功了,10秒之后,token失效,调用订单微服务,返回401

 

 改造客户端应用,在调用微服务前,判断如果token失效,就去刷新token

在客户端admin里,调用微服务前,会从session中取出token信息,放在请求头,此时的token可能是已经过期的,在这里处理刷新令牌

改造前,是直接从session中拿出token,放在zuul请求头的,改造后的 SessionTokenFilter :

package com.nb.security.admin;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;

/**
 * 从session获取token,统一加到请求头中去
 */
@Component
public class SessionTokenFilter extends ZuulFilter {

    private RestTemplate restTemplate = new RestTemplate();

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

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

    @Override
    public Object run() throws ZuulException {
        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        AccessToken accessToken = (AccessToken)request.getSession().getAttribute("token");

        if(accessToken != null){
            //token值,如果没过期就用Access_token
            String tokenValue = accessToken.getAccess_token();
            //如果token已过期,拿refresh_token换取新的access_token
            if(accessToken.isExpired()){
                String oauthServiceUrl = "http://gateway.nb.com:9070/token/oauth/token";
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);//不是json请求
                //网关的appId,appSecret,需要在数据库oauth_client_details注册
                headers.setBasicAuth("admin","123456");

                MultiValueMap<String,String> params = new LinkedMultiValueMap<>();
                params.add("refresh_token",accessToken.getRefresh_token());//授权码
                params.add("grant_type","refresh_token");//授权类型-刷新令牌


                HttpEntity<MultiValueMap<String,String>> entity = new HttpEntity<>(params,headers);
                ResponseEntity<AccessToken> newToken = restTemplate.exchange(oauthServiceUrl, HttpMethod.POST, entity, AccessToken.class);

                request.getSession().setAttribute("token",newToken.getBody().init());//调一下init方法,设置过期时间
                //token值,如果过期了,就设置成新获取的token
                tokenValue = newToken.getBody().getAccess_token();
            }

            requestContext.addZuulRequestHeader("Authorization","Bearer "+tokenValue);
        }
        return null;
    }
}

AccessToken 类也改造了:

package com.nb.security.admin;

import lombok.Data;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * access_token
 * Created by: 李浩洋 on 2020-01-02
 **/
@Data
public class AccessToken {

    private String access_token;

    private String refresh_token;

    private String token_type;

    private Long expires_in; //过期时间 秒

    private String scope;

    private LocalDateTime expireTime; //过期时间

    //设置token的失效日期
    public AccessToken init(){
        //expires_in -3 秒,在token失效之前就失效
        expireTime = LocalDateTime.now().plusSeconds(expires_in -3);
        return this;
    }
    //令牌是否过期
    public boolean isExpired(){
        return expireTime.isBefore(LocalDateTime.now());
    }


}

客户端应用表里,添加刷新令牌授权类型

 

 改造认证服务器使其支持refresh_token

 

 重启四个微服务

 

 重新登录客户端应用,连续点击获取订单信息微服务,可以看到请求,隔10秒就有一个请求时间比较长的,这就是token失效后,又去认证服务器刷新令牌的请求耗时多。

 

 认证服务器日志也有token失效记录

 

token表里也有的refresh_token

 

 

本节github  :https://github.com/lhy1234/springcloud-security/tree/chapt-5-5-refreshtoken  如果对你帮助了,给个小星星吧。

Guess you like

Origin www.cnblogs.com/lihaoyang/p/12154084.html