springcloud閘道器攔截+redis+自定義token做登入驗證操作
阿新 • • 發佈:2018-12-24
網上看到的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,才能訪問介面的資料
我的過期時間設定一天,我放到配置檔案裡了,過期時間可以自己隨意設定