學成在線(第16天)

 用戶認證需求分析

用戶認證需求分析

 用戶認證與受權

什麼是用戶身份認證?
用戶身份認證即用戶去訪問系統資源時系統要求驗證用戶的身份信息,身份合法方可繼續訪問。常見的用戶身份認
證表現形式有:用戶名密碼登陸,指紋打卡等方式。
什麼是用戶受權?
用戶認證經過後去訪問系統的資源,系統會判斷用戶是否擁有訪問資源的權限,只容許訪問有權限的系統資源,沒
有權限的資源將沒法訪問,這個過程叫用戶受權。前端

單點登陸需求

單點登陸(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一。
SSO的定義是在多個應用系統中,用戶只須要登陸一次就能夠訪問全部相互信任的應用系統。
下圖是SSO的示意圖,用戶登陸學成網一次便可訪問多個系統。java

 第三方認證

什麼是第三方認證(跨平臺認證)?
當須要訪問第三方系統的資源時須要首先經過第三方系統的認證(例如:微信認證),由第三方系統對用戶認證通
過,並受權資源的訪問權限。程序員

 用戶認證技術方案

單點登陸技術方案

分佈式系統要實現單點登陸,一般將認證系統獨立抽取出來,而且將用戶身份信息存儲在單獨的存儲介質,好比:
MySQL、Redis,考慮性能要求,一般存儲在Redis中,以下圖:web

單點登陸的特色是:
一、認證系統爲獨立的系統。
二、各子系統經過Http或其它協議與認證系統通訊,完成用戶認證。
三、用戶身份信息存儲在Redis集羣。redis

Java中有不少用戶認證的框架均可以實現單點登陸:
一、Apache Shiro.
二、CAS
三、Spring security CAS算法

Oauth2 認證

第三方認證技術方案最主要是解決認證協議的通用標準 問題,由於要實現 跨系統認證,各系統之間要遵循必定的
接口協議。
OAUTH協議爲用戶資源的受權提供了一個安全的、開放而又簡易的標準。同時,任何第三方均可以使用OAUTH認
證服務,任何服務提供商均可以實現自身的OAUTH認證服務,於是OAUTH是開放的。業界提供了OAUTH的多種實
現如PHP、JavaScript,Java,Ruby等各類語言開發包,大大節約了程序員的時間,於是OAUTH是簡易的。互聯網
不少服務如Open API,不少大公司如Google,Yahoo,Microsoft等都提供了OAUTH認證服務,這些都足以說明
OAUTH標準逐漸成爲開放資源受權的標準。spring

下邊分析一個Oauth2認證的例子,黑馬程序員網站使用微信認證的過程:json

Oauth2包括如下角色:
一、客戶端
自己不存儲資源,須要經過資源擁有者的受權去請求資源服務器的資源,好比:學成在線Android客戶端、學成在
線Web客戶端(瀏覽器端)、微信客戶端等。
二、資源擁有者
一般爲用戶,也能夠是應用程序,即該資源的擁有者。
三、受權服務器(也稱認證服務器)
用來對資源擁有的身份進行認證、對訪問資源進行受權。客戶端要想訪問資源須要經過認證服務器由資源擁有者授
權後方可訪問。
四、資源服務器
存儲資源的服務器,好比,學成網用戶管理服務器存儲了學成網的用戶信息,學成網學習服務器存儲了學生的學習
信息,微信的資源服務存儲了微信的用戶信息等。客戶端最終訪問資源服務器獲取資源信息。瀏覽器

Spring security Oauth2 認證解決方案

本項目採用 Spring security + Oauth2完成用戶認證及用戶受權,Spring security 是一個強大的和高度可定製的身
份驗證和訪問控制框架,Spring security 框架集成了Oauth2協議,下圖是項目認證架構圖:安全

一、用戶請求認證服務完成認證。
二、認證服務下發用戶身份令牌,擁有身份令牌表示身份合法。
三、用戶攜帶令牌請求資源服務,請求資源服務必先通過網關。
四、網關校驗用戶身份令牌的合法,不合法表示用戶沒有登陸,若是合法則放行繼續訪問。
五、資源服務獲取令牌,根據令牌完成受權。
六、資源服務完成受權則響應資源信息。

Spring Security Oauth2 研究

Oauth2 受權碼模式

Oauth2有如下受權模式:
受權碼模式(Authorization Code) 隱式受權模式(Implicit) 密碼模式(Resource Owner Password
Credentials) 客戶端模式(Client Credentials)
其中受權碼模式和密碼模式應用較多,本小節介紹受權碼模式。

申請受權碼

請求認證服務獲取受權碼:
Get請求:

localhost:40400/auth/oauth/authorize?
client_id=XcWebApp&response_type=code&scop=app&redirect_uri=http://localhost

參數列表以下:
client_id:客戶端id,和受權配置類中設置的客戶端id一致。
response_type:受權碼模式固定爲code
scop:客戶端範圍,和受權配置類中設置的scop一致。
redirect_uri:跳轉uri,當受權碼申請成功後會跳轉到此地址,並在後邊帶上code參數(受權碼)。

首先跳轉到登陸頁面:

 輸入帳號和密碼,點擊 Login。

接下來進入受權頁面:

點擊「贊成」。
接下來返回受權碼:
認證服務攜帶受權碼跳轉redirect_uri

 申請令牌

拿到受權碼後,申請令牌。

Post請求:http://localhost:40400/auth/oauth/token

參數以下:
grant_type:受權類型,填寫authorization_code,表示受權碼模式
code:受權碼,就是剛剛獲取的受權碼,注意:受權碼只使用一次就無效了,須要從新申請。
redirect_uri:申請受權碼時的跳轉url,必定和申請受權碼時用的redirect_uri一致。
此連接須要使用 http Basic認證。
什麼是http Basic認證?
http協議定義的一種認證方式,將客戶端id和客戶端密碼按照「客戶端ID:客戶端密碼」的格式拼接,並用base64編
碼,放在header中請求服務端

 

 

點擊發送:
申請令牌成功:

access_token:訪問令牌,攜帶此令牌訪問資源
token_type:有MAC Token與Bearer Token兩種類型,兩種的校驗算法不一樣,RFC 6750建議Oauth2採用 Bearer
Token(http://www.rfcreader.com/#rfc6750)。
refresh_token:刷新令牌,使用此令牌能夠延長訪問令牌的過時時間。
expires_in:過時時間,單位爲秒。
scope:範圍,與定義的客戶端範圍一致。

資源服務受權

資源服務擁有要訪問的受保護資源,客戶端攜帶令牌訪問資源服務,若是令牌合法則可成功訪問資源服務中的資
源,以下圖:

 上圖的業務流程以下:

1 、客戶端請求認證服務申請令牌
二、認證服務生成令牌
認證服務採用非對稱加密算法,使用私鑰生成令牌。
三、客戶端攜帶令牌訪問資源服務
客戶端在Http header 中添加: Authorization:Bearer 令牌。
四、資源服務請求認證服務校驗令牌的有效性
資源服務接收到令牌,使用公鑰校驗令牌的合法性。
五、令牌有效,資源服務向客戶端響應資源信息

JWT 研究

JWT介紹

使用JWT的思路是,用戶認證經過會獲得一個JWT令牌,JWT令牌中已經包括了用戶相關的信息,客戶端只須要攜帶
JWT訪問資源服務,資源服務根據事先約定的算法自行完成令牌校驗,無需每次都請求認證服務完成受權。
JWT令牌受權過程以下圖:

什麼是JWT?
JSON Web Token(JWT)是一個開放的行業標準(RFC 7519),它定義了一種簡介的、自包含的協議格式,用於
在通訊雙方傳遞json對象,傳遞的信息通過數字簽名能夠被驗證和信任。JWT可使用HMAC算法或使用RSA的公
鑰/私鑰對來簽名,防止被篡改。

JWT令牌的優勢:
一、jwt基於json,很是方便解析。
二、能夠在令牌中自定義豐富的內容,易擴展。
三、經過非對稱加密算法及數字簽名技術,JWT防止篡改,安全性高。
四、資源服務使用JWT可不依賴認證服務便可完成受權。
缺點:
1、JWT令牌較長,佔存儲空間比較大。

生成私鑰和公鑰

JWT令牌生成採用非對稱加密算法
一、生成密鑰證書
下邊命令生成密鑰證書,採用RSA 算法每一個證書包含公鑰和私鑰
keytool -genkeypair -alias xckey -keyalg RSA -keypass xuecheng -keystore xc.keystore -storepass
xuechengkeystore
Keytool 是一個java提供的證書管理工具
-alias:密鑰的別名
-keyalg:使用的hash算法
-keypass:密鑰的訪問密碼
-keystore:密鑰庫文件名,xc.keystore保存了生成的證書
-storepass:密鑰庫的訪問密碼

查詢證書信息:
keytool -list -keystore xc.keystore
刪除別名
keytool -delete -alias xckey -keystore xc.keystore

生成jwt令牌

在認證工程建立測試類,測試jwt令牌的生成與驗證。

/生成一個jwt令牌
@Test
public void testCreateJwt(){
    //證書文件
    String key_location = "xc.keystore";
    //密鑰庫密碼
    String keystore_password = "xuechengkeystore";
    //訪問證書路徑
    ClassPathResource resource = new ClassPathResource(key_location);
    //密鑰工廠
    KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,
keystore_password.toCharArray());
    //密鑰的密碼,此密碼和別名要匹配
 String keypassword = "xuecheng";
    //密鑰別名
    String alias = "xckey";
    //密鑰對(密鑰和公鑰)
    KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias,keypassword.toCharArray());
    //私鑰
    RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate();
    //定義payload信息
    Map<String, Object> tokenMap = new HashMap<>();
    tokenMap.put("id", "123");
    tokenMap.put("name", "mrt");
    tokenMap.put("roles", "r01,r02");
    tokenMap.put("ext", "1");
    //生成jwt令牌
    Jwt jwt = JwtHelper.encode(JSON.toJSONString(tokenMap), new RsaSigner(aPrivate));
    //取出jwt令牌
    String token = jwt.getEncoded();
    System.out.println("token="+token);
}
View Code

驗證jwt令牌

//資源服務使用公鑰驗證jwt的合法性,並對jwt解碼
    @Test
    public void testVerify(){
        //jwt令牌
        String token
="eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1ydCIsI
mlkIjoiMTIzIn0.KK7_67N5d1Dthd1PgDHMsbi0UlmjGRcm_XJUUwseJ2eZyJJWoPP2IcEZgAU3tUaaKEHUf9wSRwaDgwhrw
fyIcSHbs8oy3zOQEL8j5AOjzBBs7vnRmB7DbSaQD7eJiQVJOXO1QpdmEFgjhc_IBCVTJCVWgZw60IEW1_Lg5tqaLvCiIl26K
48pJB5f‐le2zgYMzqR1L2LyTFkq39rG57VOqqSCi3dapsZQd4ctq95SJCXgGdrUDWtD52rp5o6_0uq‐
mrbRdRxkrQfsa1j8C5IW2‐T4eUmiN3f9wF9JxUK1__XC1OQkOn‐ZTBCdqwWIygDFbU7sf6KzfHJTm5vfjp6NIA";
        //公鑰
        String publickey = "‐‐‐‐‐BEGIN PUBLIC KEY‐‐‐‐‐
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAijyxMdq4S6L1Af1rtB8SjCZHNgsQG8JTfGy55eYvzG0B/E4AudR2
prSRBvF7NYPL47scRCNPgLnvbQczBHbBug6uOr78qnWsYxHlW6Aa5dI5NsmOD4DLtSw8eX0hFyK5Fj6ScYOSFBz9cd1nNTvx
2+oIv0lJDcpQdQhsfgsEr1ntvWterZt/8r7xNN83gHYuZ6TM5MYvjQNBc5qC7Krs9wM7UoQuL+s0X6RlOib7/mcLn/lFLsLD
dYQAZkSDx/6+t+1oHdMarChIPYT1sx9Dwj2j2mvFNDTKKKKAq0cv14Vrhz67Vjmz2yMJePDqUi0JYS2r0iIo7n8vN7s83v5u
OQIDAQAB‐‐‐‐‐END PUBLIC KEY‐‐‐‐‐";
        //校驗jwt
        Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey));
        //獲取jwt原始內容
        String claims = jwt.getClaims();
        //jwt令牌
        String encoded = jwt.getEncoded();
        System.out.println(encoded);
    }

 認證接口開發

需求分析

用戶登陸的流程圖以下:

執行流程:
一、用戶登陸,請求認證服務
二、認證服務認證經過,生成jwt令牌,將jwt令牌及相關信息寫入Redis,而且將身份令牌寫入cookie
三、用戶訪問資源頁面,帶着cookie到網關
四、網關從cookie獲取token,並查詢Redis校驗token,若是token不存在則拒絕訪問,不然放行
五、用戶退出,請求認證服務,清除redis中的token,而且刪除cookie中的token

使用redis存儲用戶的身份令牌有如下做用:
一、實現用戶退出註銷功能,服務端清除令牌後,即便客戶端請求攜帶token也是無效的。
二、因爲jwt令牌過長,不宜存儲在cookie中,因此將jwt令牌存儲在redis,由客戶端請求服務端獲取並在客戶端存
儲。

Redis 配置

redis 鏈接配置

在認證服務的application.yml文件中添加以下配置:

spring:
  application:
    name: xc‐service‐ucenter‐auth
  redis:
    host: ${REDIS_HOST:127.0.0.1}
    port: ${REDIS_PORT:6379}
    timeout: 5000 #鏈接超時 毫秒
    jedis:
      pool:
        maxActive: 3
        maxIdle: 3
        minIdle: 1
        maxWait: ‐1 #鏈接池最大等行時間 ‐1沒有限制

測試

@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisTest {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Test
    public void testRedis(){
        //定義key
        String key = "user_token:9734b68f‐cf5e‐456f‐9bd6‐df578c711390";
        //定義Map
        Map<String,String> mapValue = new HashMap<>();
        mapValue.put("id","101");
        mapValue.put("username","itcast");
        String value = JSON.toJSONString(mapValue);
        //向redis中存儲字符串
        stringRedisTemplate.boundValueOps(key).set(value,60, TimeUnit.SECONDS);
        //讀取過時時間,已過時返回‐2
        Long expire = stringRedisTemplate.getExpire(key);
        //根據key獲取value
        String s = stringRedisTemplate.opsForValue().get(key);
        System.out.println(s);
    }
}

認證服務

認證服務須要實現的功能以下:
一、登陸接口
前端post提交帳號、密碼等,用戶身份校驗經過,生成令牌,並將令牌存儲到redis。
將令牌寫入cookie。
二、退出接口
校驗當前用戶的身份爲合法而且爲已登陸狀態。
將令牌從redis刪除。
刪除cookie中的令牌。

業務流程以下:

 Api接口

@Api(value = "用戶認證",description = "用戶認證接口")
public interface AuthControllerApi {
    @ApiOperation("登陸")
    public LoginResult login(LoginRequest loginRequest);
    @ApiOperation("退出")
    public ResponseResult logout();
}

配置參數

在application.yml中配置參數

auth:
  tokenValiditySeconds: 1200  #token存儲到redis的過時時間
  clientId: XcWebApp
  clientSecret: XcWebApp
  cookieDomain: localhost
  cookieMaxAge: ‐1

申請令牌測試

@SpringBootTest
@RunWith(SpringRunner.class)
public class TestClient {
    @Autowired
    LoadBalancerClient loadBalancerClient;
    @Autowired
    RestTemplate restTemplate;
    @Test
    public void testClient(){
        //採用客戶端負載均衡,從eureka獲取認證服務的ip 和端口
        ServiceInstance serviceInstance =
loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
        URI uri = serviceInstance.getUri();
        String authUrl = uri+"/auth/oauth/token";
//URI url, HttpMethod method, HttpEntity<?> requestEntity, Class<T> responseType
        // url就是 申請令牌的url /oauth/token
        //method http的方法類型
        //requestEntity請求內容
        //responseType,將響應的結果生成的類型
        //請求的內容分兩部分
        //一、header信息,包括了http basic認證信息
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
        String httpbasic = httpbasic("XcWebApp", "XcWebApp");
        //"Basic WGNXZWJBcHA6WGNXZWJBcHA="
        headers.add("Authorization", httpbasic);
        //二、包括:grant_type、username、passowrd
        MultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
        body.add("grant_type","password");
        body.add("username","itcast");
        body.add("password","123");
        HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new
HttpEntity<MultiValueMap<String, String>>(body, headers);
//指定 restTemplate當遇到400或401響應時候也不要拋出異常,也要正常返回值        
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                //當響應的值爲400或401時候也要正常響應,不要拋出異常
                if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
                    super.handleError(response);
                }
            }
        });
        //遠程調用申請令牌
        ResponseEntity<Map> exchange = restTemplate.exchange(authUrl, HttpMethod.POST,
multiValueMapHttpEntity, Map.class);
        Map body1 = exchange.getBody();
        System.out.println(body1);
    }
    private String httpbasic(String clientId,String clientSecret){
        //將客戶端id和客戶端密碼拼接,按「客戶端id:客戶端密碼」
        String string = clientId+":"+clientSecret;
        //進行base64編碼
        byte[] encode = Base64.encode(string.getBytes());
        return "Basic "+new String(encode);
    }
}
View Code

 Service

調用認證服務申請令牌,並將令牌存儲到 redis。
一、AuthToken
建立 AuthToken模型類,存儲申請的令牌,包括身份令牌、刷新令牌、jwt令牌
身份令牌:用於校驗用戶是否定證
刷新令牌:jwt令牌快過時時執行刷新令牌
jwt令牌:用於受權

@Data
@ToString
@NoArgsConstructor
public class AuthToken {
    String access_token;//身份token
    String refresh_token;//刷新token
    String jwt_token;//jwt令牌
}

申請令牌的service方法以下:

@Service
public class AuthService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AuthService.class);
    @Value("${auth.tokenValiditySeconds}")
    int tokenValiditySeconds;
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    LoadBalancerClient loadBalancerClient;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    //認證方法
    public AuthToken login(String username,String password,String clientId,String clientSecret){
        //申請令牌
        AuthToken authToken = applyToken(username,password,clientId, clientSecret);
        if(authToken == null){
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        //將 token存儲到redis
        String access_token = authToken.getAccess_token();
        String content = JSON.toJSONString(authToken);
        boolean saveTokenResult = saveToken(access_token, content, tokenValiditySeconds);
        if(!saveTokenResult){
 ExceptionCast.cast(AuthCode.AUTH_LOGIN_TOKEN_SAVEFAIL);
        }
        return authToken;
    }
    //存儲令牌到redis
    private boolean saveToken(String access_token,String content,long ttl){
        //令牌名稱
        String name = "user_token:" + access_token;
        //保存到令牌到redis
        stringRedisTemplate.boundValueOps(name).set(content,ttl, TimeUnit.SECONDS);
        //獲取過時時間
        Long expire = stringRedisTemplate.getExpire(name);
        return expire>0;
    }
    //認證方法
    private AuthToken applyToken(String username,String password,String clientId,String
clientSecret){
        //選中認證服務的地址
        ServiceInstance serviceInstance =
loadBalancerClient.choose(XcServiceList.XC_SERVICE_UCENTER_AUTH);
        if (serviceInstance == null) {
            LOGGER.error("choose an auth instance fail");
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_AUTHSERVER_NOTFOUND);
        }
        //獲取令牌的url
        String path = serviceInstance.getUri().toString()+"/auth/oauth/token";
        //定義body
        MultiValueMap<String,String> formData = new LinkedMultiValueMap<>();
        //受權方式
        formData.add("grant_type", "password");
        //帳號
        formData.add("username",username);
        //密碼
        formData.add("password", password);
        //定義頭
        MultiValueMap<String,String> header = new LinkedMultiValueMap<>();
        header.add("Authorization", httpbasic(clientId,clientSecret));
        //指定 restTemplate當遇到400或401響應時候也不要拋出異常,也要正常返回值
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                //當響應的值爲400或401時候也要正常響應,不要拋出異常
                if(response.getRawStatusCode()!=400 && response.getRawStatusCode()!=401){
                    super.handleError(response);
                }
            }
        });
        Map map null;
        try {
            //http請求spring security的申請令牌接口
            ResponseEntity<Map> mapResponseEntity = restTemplate.exchange(path, HttpMethod.POST,new HttpEntity<MultiValueMap<String, String>>(formData, header), Map.class);
            map = mapResponseEntity.getBody();
        } catch (RestClientException e) {
            e.printStackTrace();
            LOGGER.error("request oauth_token_password error: {}",e.getMessage());
            e.printStackTrace();
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        if(map == null ||
                map.get("access_token") == null ||
                map.get("refresh_token") == null ||
                map.get("jti") == null){//jti是jwt令牌的惟一標識做爲用戶身份令牌
            ExceptionCast.cast(AuthCode.AUTH_LOGIN_APPLYTOKEN_FAIL);
        }
        AuthToken authToken new AuthToken();
        //訪問令牌(jwt)
        String jwt_token = (String) map.get("access_token");
        //刷新令牌(jwt)
        String refresh_token = (String) map.get("refresh_token");
        //jti,做爲用戶的身份標識
        String access_token = (String) map.get("jti");
        authToken.setJwt_token(jwt_token);
        authToken.setAccess_token(access_token);
        authToken.setRefresh_token(refresh_token);
        return authToken;
    }
    //獲取httpbasic認證串
   private String httpbasic(String clientId,String clientSecret){
        //將客戶端id和客戶端密碼拼接,按「客戶端id:客戶端密碼」
        String string = clientId+":"+clientSecret;
        //進行base64編碼
        byte[] encode = Base64.encode(string.getBytes());
        return "Basic "+new String(encode);
    }
}
View Code

Controller

AuthController代碼以下:

@RestController
public class AuthController implements AuthControllerApi {
    @Value("${auth.clientId}")
    String clientId;
    @Value("${auth.clientSecret}")
    String clientSecret;
@Value("${auth.cookieDomain}")
    String cookieDomain;
    @Value("${auth.cookieMaxAge}")
    int cookieMaxAge;
    @Value("${auth.tokenValiditySeconds}")
    int tokenValiditySeconds;
    @Autowired
    AuthService authService;
   
    @Override
    @PostMapping("/userlogin")
    public LoginResult login(LoginRequest loginRequest) {
        //校驗帳號是否輸入
        if(loginRequest == null || StringUtils.isEmpty(loginRequest.getUsername())){
            ExceptionCast.cast(AuthCode.AUTH_USERNAME_NONE);
        }
        //校驗密碼是否輸入
        if(StringUtils.isEmpty(loginRequest.getPassword())){
            ExceptionCast.cast(AuthCode.AUTH_PASSWORD_NONE);
        }
        AuthToken authToken = authService.login(loginRequest.getUsername(),
loginRequest.getPassword(), clientId, clientSecret);
        //將令牌寫入cookie
        //訪問token
        String access_token = authToken.getAccess_token();
        //將訪問令牌存儲到cookie
        saveCookie(access_token);
        return new LoginResult(CommonCode.SUCCESS,access_token);
    }
    //將令牌保存到cookie
    private void saveCookie(String token){
        HttpServletResponse response = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getResponse();
        //添加cookie 認證令牌,最後一個參數設置爲false,表示容許瀏覽器獲取
        CookieUtil.addCookie(response, cookieDomain, "/", "uid", token, cookieMaxAge, false);
    }
   
    @Override
    @PostMapping("/userlogout")
    public ResponseResult logout() {
        return null;
    }
}
View Code

 登陸url放行

認證服務默認都要校驗用戶的身份信息,這裏須要將登陸url放行。

在 WebSecurityConfig類中重寫 configure(WebSecurity web)方法,以下:

@Override    
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/userlogin");
    }

測試認證接口

使用postman測試:
Post請求:http://localhost:40400/auth/userlogin

 

 

相關文章
相關標籤/搜索