一、登錄接口具體實現
完成上一篇測試以后,表示我們搭建的認證服務器可以生成令牌,這時候我們需要再此基礎上寫登錄邏輯
登錄接口
前端post提交賬號、密碼等,用戶身份效驗通過,生成令牌,並將令牌存儲到redis當中,具體業務流程圖如下

創建AuthControllerApi.java
@Api(tags = "用戶認證API接口", description = "用戶登錄認證接口") public interface AuthControllerApi { @ApiOperation(value = "登錄") @PostMapping("/userLogin") public ResultVo login(LoginRequest loginRequest); @ApiOperation(value = "退出") public ResultVo logout(); }
創建AuthController.java
@RestController public class AuthController implements AuthControllerApi{ @Autowired private AuthService authService; @Autowired private SecurityAuth securityAuth; @Override @PostMapping("/userLogin") public ResultVo login(LoginRequest loginRequest) { //申請令牌 ResultVo resultVo=authService.login(loginRequest,securityAuth.getClientId(),securityAuth.getClientSecret()); return resultVo; } }
創建AuthService.java
@Service public class AuthService { @Autowired private StringRedisTemplate stringRedisTemplate; @Autowired private SecurityAuth securityAuth; public ResultVo login(LoginRequest loginRequest, String clientId, String clientSecret) { //用戶賬號和密碼的非空判斷 if (!Exist.isExists(loginRequest) || !Exist.isExists(loginRequest.getPassword())) { return ResultVo.response(AuthKeFeiEnums.FAIL_NULL_PASSWORD); } if (!Exist.isExists(loginRequest) || !Exist.isExists(loginRequest.getUsername())) { return ResultVo.response(AuthKeFeiEnums.FAIL_NULL_USERNAME); } //獲取客戶賬號和密碼 String username = loginRequest.getUsername(); String password = loginRequest.getPassword(); //請求security獲取令牌 AuthToken authToken = applyToken(username, password, clientId, clientSecret); if (!Exist.isExists(authToken)) { return ResultVo.response(AuthKeFeiEnums.FAIL_AUTH_APPLYTOKEN_NULL); } //獲取用戶身份令牌 String access_token = authToken.getAccess_token(); //存儲到redis中的內容 String jsonString = JSON.toJSONString(authToken); //將令牌存儲到redis當中 boolean result = this.saveToken(access_token, jsonString, securityAuth.getTokenValiditySeconds()); if (!result) { return ResultVo.response(AuthKeFeiEnums.FAIL_SAVE_AUTH_TOKEN); } //將令牌存儲到cookie addCookie(securityAuth.getCookieDomain(), "/", "uid", access_token, securityAuth.getCookieMaxAge(), false); return ResultVo.response(AuthKeFeiEnums.SUCCESS, access_token); }
/** * @param clientId 客戶端賬號 * @param clientSecret 客戶端密碼 * @return java.lang.String * @author GuFei * <獲取httpbasic的串> * @date 2019/8/28 11:52 * @version V1.0 */ private String getHttpBasic(String clientId, String clientSecret) { String ss = clientId + ":" + clientSecret; byte[] encode = Base64Utils.encode(ss.getBytes()); //Basic后面要空一個 return "Basic " + new String(encode); } /** * @param username 用戶賬號 * @param password 用戶密碼 * @param clientId 客戶端賬號 * @param clientSecret 客戶端密碼 * @return com.kefei.fremawork.dto.po.security.AuthToken * @author GuFei * <申請令牌> * @date 2019/8/28 11:53 * @version V1.0 */ private AuthToken applyToken(String username, String password, String clientId, String clientSecret) { RestTemplate restTemplate = new RestTemplate(); //定義header LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<>(); String httpBasic = getHttpBasic(clientId, clientSecret); header.add("Authorization", httpBasic); //定義body LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<>(); body.add("grant_type", "password"); body.add("username", username); body.add("password", password); HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(body, header); //String url, HttpMethod method, @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables //設置restTemplate遠程調用時候,對400和401不讓報錯,正確返回數據 restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { @Override public void handleError(ClientHttpResponse response) throws IOException { if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) { super.handleError(response); } } }); ResponseEntity<Map> exchange = restTemplate.exchange(securityAuth.getAccessTokenUri(), HttpMethod.POST, httpEntity, Map.class); //申請令牌信息 Map bodyMap = exchange.getBody(); if (bodyMap == null || bodyMap.get("access_token") == null || bodyMap.get("refresh_token") == null || bodyMap.get("jti") == null) { return null; } AuthToken authToken = new AuthToken(); //訪問令牌(jwt) String jwt_token = (String) bodyMap.get("access_token"); //刷新令牌(jwt) String refresh_token = (String) bodyMap.get("refresh_token"); //jti,作為用戶的身份標識 String access_token = (String) bodyMap.get("jti"); authToken.setJwt_token(jwt_token); authToken.setAccess_token(access_token); authToken.setRefresh_token(refresh_token); return authToken; } /* * @author GuFei * <描述內容> * @date 2019/8/28 11:53 * @param access_token 令牌 * @param content 獲取到令牌、刷新令牌、短令牌等信息 * @param ttl 存儲在redis中的有效時間 * @return boolean * @version V1.0 */ private boolean saveToken(String access_token, String content, long ttl) { //令牌名稱key String nameKey = RedisKeyGenerator.saveTokenKey(access_token); //保存令牌到redis,TimeUnit.SECONDS為設置過期時間,單位為秒 stringRedisTemplate.boundValueOps(nameKey).set(content, ttl, TimeUnit.SECONDS); //獲取過期時間 Long expire = stringRedisTemplate.getExpire(nameKey); //如果大於0,表示存儲成功,如果小於0則表示存儲失敗了 return expire > 0; } /** * @param domain * @param path * @param name cookie名字 * @param value cookie值 * @param maxAge cookie生命周期 以秒為單位 * @param httpOnly * @return void * @author GuFei * <將令牌存儲到cookie> * @date 2019/8/28 11:55 * @version V1.0 */ private void addCookie(String domain, String path, String name, String value, int maxAge, boolean httpOnly) { HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); Cookie cookie = new Cookie(name, value); cookie.setDomain(domain); cookie.setPath(path); cookie.setMaxAge(maxAge); cookie.setHttpOnly(httpOnly); response.addCookie(cookie); } }
注意:在使用postman測試的時候,一切正常但是正式環境測試異常報401,Request Method是options類型,這是因為web前端在發送請求的時候,會先發送一個options請求,作為探測請求查看請求鏈接是否建立。
解決辦法:
我們需要再資源服務里面找到繼承ResourceServerConfigurerAdapter的實體類

給與放行

至此,我們完成了認證服務器的登錄認證請求
