1. 程式人生 > >springcloud閘道器攔截+redis+自定義token做登入驗證操作

springcloud閘道器攔截+redis+自定義token做登入驗證操作

網上看到的token做起來都太複雜,介紹說耗費的記憶體較大,寫的封裝方法非常多,看來看去非常不方便,自己就藉助token思想,和閘道器攔截器組合操作的登入驗證機制。

1.下面這段程式碼就是使用者請求,驗證資料庫是否有這個使用者名稱和密碼,使用者登入成功與否,成功登入就生成token儲存到redis裡

package gsa.rest.datacenter.rest.login;

import gsa.base.common.Enum.StatusCode;
import gsa.base.common.Utils.DataResult;
import gsa.base.datasources.Redisconfig.RedisTemplete;
import gsa.rest.datacenter.service.login.LoginService;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.UUID;

/**
 * Created by df on 2018/9/28.
 */
@Controller
@Slf4j
@CrossOrigin
@RequestMapping("/login")
public class LoginController {
    private static final Logger log = LoggerFactory.getLogger(LoginController.class);
    @Autowired
    private LoginService loginService;

    @Autowired
    private RedisTemplete redisTemplete;

    @Value("${redis.expireTime}")
    private Long expireTime;//token過期時間

    @ApiOperation(value = "登入驗證")
    @GetMapping(path = "/loginValidate")
    @ResponseBody
    public DataResult login(String username, String password, HttpServletResponse response) throws Exception {
        try {
            List list = loginService.login(username, password);
            if (list.size() > 0) {
                String token = TokenStorage(response, username);
                log.info("登入成功,儲存token");
                return new DataResult("登入成功", StatusCode.SUCCESS.getCode(), "Bearer "+token);
            } else {
                log.info("登入失敗,使用者名稱或密碼錯誤");
                return new DataResult("使用者名稱或密碼錯誤", StatusCode.SUCCESS.getCode());
            }
        } catch ( Exception e ) {
            e.printStackTrace();
        }
        return new DataResult(StatusCode.ERROR.getMessage(), StatusCode.ERROR.getCode());
    }

    public String TokenStorage(HttpServletResponse response, String username) {
        //生成token
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        //儲存redis裡
        redisTemplete.set(token, username, expireTime);
        return token;
    }

}

 2.下面登入完畢就該攔截一些介面了,登入的才可以訪問介面,沒有的判斷token做相應的操作,我的攔截器是springcloud自帶的攔截器,起到各個微服務統一閘道器的作用

package gsa.portal.gateway.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import gsa.base.datasources.Redisconfig.RedisTemplete;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;


/**
 * Created by df on 2018/10/8.
 * 攔截器,如果return null 正常訪問各個微服務的介面
 * 如果被攔截器攔截將會返回處理的資訊,也將不會訪問日誌記錄
 */
@Component
public class PreZuulFilter extends ZuulFilter {
    private static final Logger log = LoggerFactory.getLogger(ZuulFilter.class);
    @Bean
    private RedisTemplete redisTemplete(){
        return new RedisTemplete();
    }
    @Autowired
    private RedisTemplete redisTemplete;

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String url = request.getRequestURL().toString();
        //獲取認證名稱
        String Authname =request.getHeader("Authorization");
        String token=null;
        if(Authname!=null&&!Authname.equals("")){
            //使用者請求時會在頭部 Authorization 傳給我之前儲存的token, 我用來驗證
            Authname= Authname.replace("Bearer ","");
            //獲取redis儲存的token
            if (redisTemplete.exists(Authname)){
                //查詢redis是否有token
                token= (String) redisTemplete.get(Authname);
            }
        }
        //此處判斷是否要攔截**************
        //過濾登入方法
        if(url.contains("/login/loginValidate")){
             return null;
        }
        //過濾datacenter微服務
        if(url.contains("/gsa/rest/")){
            if(!url.contains("/MenuSystemTree")){
                return null;
            }
        }
        //過濾es微服務
        if(url.contains("/gsa/tool/")) {
            return null;
//            if (redisTemplete.exists("INTERFACE_FILTER_ES")) {
//                if (redisTemplete.get("INTERFACE_FILTER_ES").equals("FALSE")) {
//                }
//            }
        }
        //*******************開始攔截****************************
        log.info(String.format("%s  攔截的url: %s",request.getMethod(),url));
        //沒有加認證token 就沒有訪問許可權
        if(StringUtils.isBlank(Authname)){
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("{\"code\":401,\"msg\":\"沒有訪問許可權!\"}");
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
        }else if(token==null){
            //token失效了
            //使用者提供的token檢測出和redis不一樣
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.setResponseBody("{\"code\":401,\"msg\":\"令牌失效,請重新登入!\"}");
            ctx.getResponse().setContentType("text/html;charset=UTF-8");
        }
        //*******************結束攔截****************************
        //ctx.addZuulRequestHeader("username", username);
        return null;
    }
    @Override
    public String filterType() {
        return "pre";
    }

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

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

 3.訪問登入介面返回的資料,將data裡的資料加到請求的頭部
header{
Authorization:Bearer 60a12d1f892245e5b70c9c84494b4810
}

 4.呼叫另一個介面,輸入錯誤的token情況下

5.請求中沒有加token的情況下

 5.直到輸入正確的token,才能訪問介面的資料

我的過期時間設定一天,我放到配置檔案裡了,過期時間可以自己隨意設定