久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

OAuth2,、CAS單點(diǎn)登錄

 愛(ài)吃魚的俊懶貓 2020-12-22

一,、Oauth 是一個(gè)關(guān)于授權(quán)(authorization)的開網(wǎng)絡(luò)標(biāo)準(zhǔn)(規(guī)范)

OAuth2: 解決的是不同的企業(yè)之間的登錄,,本質(zhì)是授權(quán),,如論壇與QQ

要能訪問(wèn)各種資源重點(diǎn)是要獲取令牌(token),,但根據(jù)令牌的獲取方式不同,,又會(huì)有四種授權(quán)方式

  1. 授權(quán)碼(authorization-code)
  2. 隱藏式(implicit)
  3. 密碼式(password)
  4. 客戶端憑證(client credentials)

授權(quán)碼這是最常用的一種方式,指的是第三方應(yīng)用先申請(qǐng)一個(gè)授權(quán)碼,,然后再用該碼獲取令牌,,項(xiàng)目中用的就是這種

隱藏式:允許直接向前端頒發(fā)令牌。這種方式?jīng)]有授權(quán)碼這個(gè)中間步驟,,所以稱為(授權(quán)碼)"隱藏式"(implicit),,一般應(yīng)用于純前端項(xiàng)目

密碼式:直接通過(guò)用戶名和密碼的方式申請(qǐng)令牌,這方式是最不安全的方式

憑證式這種方式的令牌是針對(duì)第三方應(yīng)用,,而不是針對(duì)用戶的,,既某個(gè)第三方應(yīng)用的所有用戶共用一個(gè)令牌,一般用于沒(méi)有前端的命令行應(yīng)用

授權(quán)碼授權(quán)流程:

第一步,,A 網(wǎng)站提供一個(gè)鏈接,,用戶點(diǎn)擊后就會(huì)跳轉(zhuǎn)到 B 網(wǎng)站(權(quán)限驗(yàn)證系統(tǒng))

http:///oauth/authorize?

  response_type=code&

  client_id=CLIENT_ID&

  redirect_uri=CALLBACK_URL&

  scope=read

用戶跳轉(zhuǎn)后,,B 網(wǎng)站如果沒(méi)有登錄會(huì)要求用戶登錄,,然后詢問(wèn)是否同意給予 A 網(wǎng)站授權(quán)。用戶表示同意,,這時(shí) B 網(wǎng)站就會(huì)跳回redirect_uri參數(shù)指定的網(wǎng)址,,并附加授權(quán)碼code

http://a.com/callback?code=AUTHORIZATION_CODE

第三步,A 網(wǎng)站拿到授權(quán)碼以后,在后端,,向 B 網(wǎng)站請(qǐng)求令牌。

http:///oauth/token?

 client_id=CLIENT_ID&

 client_secret=CLIENT_SECRET&

 grant_type=authorization_code&

 code=AUTHORIZATION_CODE&

 redirect_uri=CALLBACK_URL

上面 URL 中,,client_id參數(shù)和client_secret參數(shù)用來(lái)讓 B 確認(rèn) A 的身份(client_secret參數(shù)是保密的,,因此只能在后端發(fā)請(qǐng)求),grant_type參數(shù)的值是AUTHORIZATION_CODE,,表示采用的授權(quán)方式是授權(quán)碼,,code參數(shù)是上一步拿到的授權(quán)碼,redirect_uri參數(shù)是令牌頒發(fā)后的回調(diào)網(wǎng)址,。

第四步,,B 網(wǎng)站收到請(qǐng)求以后,就會(huì)頒發(fā)令牌,。具體做法是向redirect_uri指定的網(wǎng)址,,發(fā)送一段 JSON 數(shù)據(jù)。

{    

  "access_token":"ACCESS_TOKEN",

  "info":{...}

}

接下來(lái)用戶就可以根據(jù)這個(gè)access_token來(lái)進(jìn)行訪問(wèn)了,,

如A網(wǎng)站拿著token,,申請(qǐng)獲取用戶信息,B網(wǎng)站確認(rèn)令牌無(wú)誤,,同意向A網(wǎng)站開放資源,。

對(duì)于第三方網(wǎng)站來(lái)說(shuō) 可分為3部分

1、申請(qǐng)code

2,、申請(qǐng)token

3,、帶著token去請(qǐng)求資源(如:申請(qǐng)獲取用戶信息)

偽代碼

服務(wù)端

@RequestMapping("authorize")
public Object authorize(Model model, HttpServletRequest request) throws OAuthSystemException, URISyntaxException {
    //構(gòu)建OAuth請(qǐng)求
    OAuthAuthzRequest oAuthAuthzRequest = null;
    try {
        oAuthAuthzRequest = new OAuthAuthzRequest(request);

        // 根據(jù)傳入的clientId 判斷 客戶端是否存在
        if(!authorizeService.checkClientId(oAuthAuthzRequest.getClientId())) {
            return HttpResponseBody.failResponse("客戶端驗(yàn)證失敗,如錯(cuò)誤的client_id/client_secret");
        }

        // 判斷用戶是否登錄
        Subject subject = SecurityUtils.getSubject();

        if(!subject.isAuthenticated()) {
            if(!login(subject, request)) {
                return new HttpResponseBody(ResponseCodeConstant.UN_LOGIN_ERROR, "沒(méi)有登陸");
            }
        }
        String username = (String) subject.getPrincipal();

        //生成授權(quán)碼
        String authorizationCode = null;

        String responseType = oAuthAuthzRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
        if(responseType.equals(ResponseType.CODE.toString())) {
            OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(new MD5Generator());
            authorizationCode = oAuthIssuer.authorizationCode();
            shiroCacheUtil.addAuthCode(authorizationCode, username);
        }

        Map<String, Object> data = new HashMap<>();
        data.put(SsoConstants.AUTH_CODE, authorizationCode);
        return HttpResponseBody.successResponse("ok", data);

    } catch(OAuthProblemException e) {
        return HttpResponseBody.failResponse(e.getMessage());
    }
}
@RequestMapping("/accessToken")
public HttpEntity token(HttpServletRequest request) throws OAuthSystemException {
    try {
        // 構(gòu)建Oauth請(qǐng)求
        OAuthTokenRequest oAuthTokenRequest = new OAuthTokenRequest(request);
        
        //檢查提交的客戶端id是否正確
        if(!authorizeService.checkClientId(oAuthTokenRequest.getClientId())) {
            OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                    .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                    .setErrorDescription("客戶端驗(yàn)證失敗,,如錯(cuò)誤的client_id/client_secret")
                    .buildJSONMessage();
            return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
        }
        
        // 檢查客戶端安全Key是否正確
        if(!authorizeService.checkClientSecret(oAuthTokenRequest.getClientSecret())){
            OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
                    .setErrorDescription("客戶端驗(yàn)證失敗,,如錯(cuò)誤的client_id/client_secret")
                    .buildJSONMessage();
            return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
        }
        
        String authCode = oAuthTokenRequest.getParam(OAuth.OAUTH_CODE);
        
        // 檢查驗(yàn)證類型,此處只檢查AUTHORIZATION類型,,其他的還有PASSWORD或者REFRESH_TOKEN
        if(oAuthTokenRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())){
            if(!shiroCacheUtil.checkAuthCode(authCode)){
                OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                        .setError(OAuthError.TokenResponse.INVALID_GRANT)
                        .setErrorDescription("error grant code")
                        .buildJSONMessage();
                return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
            }
        }

        //生成Access Token
        OAuthIssuer issuer = new OAuthIssuerImpl(new MD5Generator());
        final String accessToken  = issuer.accessToken();
        shiroCacheUtil.addAccessToken(accessToken, shiroCacheUtil.getUsernameByAuthCode(authCode));
        logger.info("accessToken generated : {}", accessToken);

        //需要保存clientSessionId和clientId的關(guān)系到redis,,便于在Logout時(shí)通知系統(tǒng)logout
        String clientSessionId = request.getParameter("sid");
        //System.out.println("clientSessionId = " + clientSessionId);
        String clientId = oAuthTokenRequest.getClientId();
        //System.out.println("clientId = " + clientId);
        redisTemplate.opsForHash().put(RedisKey.CLIENT_SESSIONS, clientSessionId, clientId);

        // 生成OAuth響應(yīng)
        OAuthResponse response = OAuthASResponse.tokenResponse(HttpServletResponse.SC_OK)
                .setAccessToken(accessToken).setExpiresIn(String.valueOf(authorizeService.getExpireIn()))
                .buildJSONMessage();
        
        return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
    } catch(OAuthProblemException e) {
        e.printStackTrace();
            OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e).buildBodyMessage();
        return new ResponseEntity<>(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
    }
}
@RequestMapping("/userInfo")
public HttpEntity userInfo(HttpServletRequest request) throws OAuthSystemException {
    try {
        
        //構(gòu)建OAuth資源請(qǐng)求
        OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
        //獲取Access Token
        String accessToken = oauthRequest.getAccessToken();

        //驗(yàn)證Access Token
        if (!shiroCacheUtil.checkAccessToken(accessToken)) {
            // 如果不存在/過(guò)期了,返回未驗(yàn)證錯(cuò)誤,,需重新驗(yàn)證
            OAuthResponse oauthResponse = OAuthRSResponse
                    .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm("fxb")
                    .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
                    .buildHeaderMessage();
            
            HttpHeaders headers = new HttpHeaders();
            headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
            return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
        }
        //返回用戶名
        String username = shiroCacheUtil.getUsernameByAccessToken(accessToken);
        SysUser user = userService.selectByAccount(username);
        return new ResponseEntity<>(user, HttpStatus.OK);
    } catch (OAuthProblemException e) {
        //檢查是否設(shè)置了錯(cuò)誤碼
        String errorCode = e.getError();
        if (OAuthUtils.isEmpty(errorCode)) {
            OAuthResponse oauthResponse = OAuthRSResponse
                    .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                    .setRealm("fxb")
                    .buildHeaderMessage();
            
            HttpHeaders headers = new HttpHeaders();
            headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
            return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
        }
        
        OAuthResponse oauthResponse = OAuthRSResponse
                .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                .setRealm("fxb")
                .setError(e.getError())
                .setErrorDescription(e.getDescription())
                .setErrorUri(e.getUri())
                .buildHeaderMessage();
        
        HttpHeaders headers = new HttpHeaders();
        headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
        return new ResponseEntity(HttpStatus.BAD_REQUEST);
    }
}

客戶端

private String extractUsername(String code) {
    OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
    try {
        OAuthClientRequest accessTokenRequest = OAuthClientRequest.tokenLocation(accessTokenUrl)
                .setGrantType(GrantType.AUTHORIZATION_CODE)
                .setClientId(clientId)
                .setClientSecret(clientSecret)
                .setCode(code)
                .setRedirectURI(redirectUrl)
                .setParameter("sid", SecurityUtils.getSubject().getSession().getId().toString())
                .buildQueryMessage();
        OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);
        String accessToken = oAuthResponse.getAccessToken();

        //拿用戶信息
        OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(userInfoUrl)
                .setAccessToken(accessToken).buildQueryMessage();

        OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);

        String userJson = resourceResponse.getBody();
        SysUser user = JsonUtils.json2Obj(userJson, SysUser.class);
        this.setResource(user, accessToken);
        return user.getUserName();
    } catch(OAuthSystemException e) {
        e.printStackTrace();
        throw new  RuntimeException(e);
    } catch(OAuthProblemException e) {
        e.printStackTrace();
        throw new BusinessException(ResponseCodeConstant.UN_LOGIN_ERROR, "沒(méi)有登錄");
    }
}
<dependency>
   <groupId>org.apache.oltu.oauth2</groupId>
   <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
   <version>1.0.2</version>
</dependency>
<dependency>
   <groupId>org.apache.oltu.oauth2</groupId>
   <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
   <version>1.0.2</version>
</dependency>

二,、單點(diǎn): 是解決企業(yè)內(nèi)部的一系列產(chǎn)品登錄問(wèn)題,安全信任度要比oauth2高

(一)session-cookie機(jī)制

1,、session-cookie機(jī)制出現(xiàn)的根源,, http連接是無(wú)狀態(tài)的連接

-------- 同一瀏覽器向服務(wù)端發(fā)送多次請(qǐng)求,服務(wù)器無(wú)法識(shí)別,,哪些請(qǐng)求是同一個(gè)瀏覽器發(fā)出的

2,、為了標(biāo)識(shí)哪些請(qǐng)求是屬于同一個(gè)人 ---------- 需要在請(qǐng)求里加一個(gè)標(biāo)識(shí)參數(shù)

方法1-----------直接在url里加一個(gè)標(biāo)識(shí)參數(shù)(對(duì)前端開發(fā)有侵入性),如: token

方法2-----------http請(qǐng)求時(shí),自動(dòng)攜帶瀏覽器的cookie(對(duì)前端開發(fā)無(wú)知覺(jué)),,如:jsessionid=XXXXXXX

3,、瀏覽器標(biāo)識(shí)在網(wǎng)絡(luò)上的傳輸,是明文的,,不安全的

-----------安全措施:改https來(lái)保障

4,、cookie的使用限制---依賴域名

-------------- 頂級(jí)域名下cookie,會(huì)被二級(jí)以下的域名請(qǐng)求,,自動(dòng)攜帶

-------------- 二級(jí)域名的cookie,,不能攜帶被其它域名下的請(qǐng)求攜帶

5、在服務(wù)器后臺(tái),,通過(guò)解讀標(biāo)識(shí)信息(token或jsessionid),,來(lái)對(duì)應(yīng)會(huì)話是哪個(gè)session

--------------- 一個(gè)tomcat,被1000個(gè)用戶登陸,,tomcat里一定有1000個(gè)session -------》存儲(chǔ)格式map《sessionid,,session對(duì)象》

--------------- 通過(guò)前端傳遞的jsessionid,來(lái)對(duì)應(yīng)取的session ------ 動(dòng)作發(fā)生時(shí)機(jī)request.getsession

(二)session共享方式,,實(shí)現(xiàn)的單點(diǎn)登陸

1,、多個(gè)應(yīng)用共用同一個(gè)頂級(jí)域名,sessionid被種在頂級(jí)域名的cookie里

2,、后臺(tái)session通過(guò)redis實(shí)現(xiàn)共享(重寫httprequest,、httpsession 或使用springsession框架),即每個(gè)tomcat都在請(qǐng)求開始時(shí),,到redis查詢session;在請(qǐng)求返回時(shí),,將自身session對(duì)象存入redis

3、當(dāng)請(qǐng)求到達(dá)服務(wù)器時(shí),,服務(wù)器直接解讀cookie中的sessionid,,然后通過(guò)sessionid到redis中查找到對(duì)應(yīng)會(huì)話session對(duì)象

4、后臺(tái)判斷請(qǐng)求是否已登陸,,主要校驗(yàn)session對(duì)象中,,是否存在登陸用戶信息

5、整個(gè)校驗(yàn)過(guò)程,,通過(guò)filter過(guò)濾器來(lái)攔截切入,,如下圖:

6、登陸成功時(shí),,后臺(tái)需要給頁(yè)面種cookie方法如下:

response里,,反映的種cookie效果如下:

7、為了request.getsession時(shí),,自動(dòng)能拿到redis中共享的session,,

    我們需要重寫request的getsession方法(使用HttpServletRequestWrapper包裝原request)

(三)cas單點(diǎn)登陸方案

1,、對(duì)于完全不同域名的系統(tǒng),cookie是無(wú)法跨域名共享的

2,、cas方案,,直接啟用一個(gè)專業(yè)的用來(lái)登陸的域名(比如:)來(lái)供所有的系統(tǒng)登陸。

3,、當(dāng)業(yè)務(wù)系統(tǒng)(如)被打開時(shí),,借助cas系統(tǒng)來(lái)登陸,過(guò)程如下:

cas登陸的全過(guò)程:

(1),、打開時(shí),發(fā)現(xiàn)自己未登陸 ----》 于是跳轉(zhuǎn)到去登陸

(2),、登陸頁(yè)面被打開,,用戶輸入帳戶/密碼登陸成功

(3)、登陸成功,,種cookie到域名下 -----------》把sessionid放入后臺(tái)redis《ticket,,sesssionid》---頁(yè)面跳回

String ticket = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(ticket,request.getSession().getId(),20, TimeUnit.SECONDS);//一定要設(shè)置過(guò)期時(shí)間
CookieBasedSession.onNewSession(request,response);
response.sendRedirect(user.getBackurl()+"?ticket="+ticket);

(4)、重新被打開,,發(fā)現(xiàn)仍然是未登陸,,但是有了一個(gè)ticket值

(5)、用ticket值,,到redis里查到sessionid,,并做session同步 ------ 》種cookie給自己,頁(yè)面原地重跳

(6),、打開自己頁(yè)面,,此時(shí)有了cookie,后臺(tái)校驗(yàn)登陸狀態(tài),,成功

(7)整個(gè)過(guò)程交互,,列圖如下:

4、的登陸頁(yè)面被打開時(shí),如果此時(shí)本來(lái)就是登陸狀態(tài)的,則自動(dòng)返回生成ticket給業(yè)務(wù)系統(tǒng)

整個(gè)單點(diǎn)登陸的關(guān)鍵部位,,是利用的cookie保持是登陸狀態(tài),此后任何第三個(gè)系統(tǒng)跳入,都將自動(dòng)完成登陸過(guò)程

5,本示例中,使用了redis來(lái)做cas的服務(wù)接口,請(qǐng)根據(jù)工作情況,自行替換為合適的服務(wù)接口(主要是根據(jù)sessionid來(lái)判斷用戶是否已登陸)

6,為提高安全性,ticket應(yīng)該使用過(guò)即作廢(本例中,會(huì)用有效期機(jī)制)

public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    MyRequestWrapper myRequestWrapper = new MyRequestWrapper(request,redisTemplate);
    //如果未登陸狀態(tài),,進(jìn)入下面邏輯
    String requestUrl = request.getServletPath();
    if (!"/toLogin".equals(requestUrl) && !requestUrl.startsWith("/login")  && !myRequestWrapper.isLogin()) {
        /**
         * ticket為空,或無(wú)對(duì)應(yīng)sessionid為空
         * --- 表明不是自動(dòng)登陸請(qǐng)求--直接強(qiáng)制到登陸頁(yè)面
         */
        String ticket = request.getParameter("ticket");
        if (null == ticket || null == redisTemplate.opsForValue().get(ticket)){
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            response.sendRedirect("http://:8090/toLogin?url="+request.getRequestURL().toString());
            return ;
        }
        /**
         * 是自動(dòng)登陸請(qǐng)求,,則種cookie值進(jìn)去---本次請(qǐng)求是302重定向
         * 重定向后的下次請(qǐng)求,,自帶本cookie,將直接是登陸狀態(tài)
         */
        myRequestWrapper.setSessionId((String) redisTemplate.opsForValue().get(ticket));
        myRequestWrapper.createSession();
        //種cookie
        CookieBasedSession.onNewSession(myRequestWrapper,(HttpServletResponse)servletResponse);
        //重定向自流轉(zhuǎn)一次,,原地跳轉(zhuǎn)重向一次
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        response.sendRedirect(request.getRequestURL().toString());
        return;
    }
    try {
        filterChain.doFilter(myRequestWrapper,servletResponse);
    } finally {
        myRequestWrapper.commitSession();
    }
}
public static void onNewSession(HttpServletRequest request, HttpServletResponse response) {
    HttpSession session = request.getSession();
    String sessionId = session.getId();
    Cookie cookie = new Cookie(COOKIE_NAME_SESSION, sessionId);
    cookie.setHttpOnly(true);
    cookie.setPath(request.getContextPath() + "/");
    cookie.setMaxAge(Integer.MAX_VALUE);
    response.addCookie(cookie);
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多