国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前后端分離項(xiàng)目 — SpringSocial 綁定與解綁社交賬號(hào)如微信、QQ

tigerZH / 1137人閱讀

摘要:我們以微信為例,首先我們發(fā)送一個(gè)請(qǐng)求,因?yàn)槟阋呀?jīng)登錄了,所以后臺(tái)可以獲取當(dāng)前是誰,然后就獲取到請(qǐng)求的鏈接,最后就是跳轉(zhuǎn)到這個(gè)鏈接上面去。

1、準(zhǔn)備工作

申請(qǐng)QQ、微信相關(guān)AppId和AppSecret,這些大家自己到QQ互聯(lián)和微信開發(fā)平臺(tái) 去申請(qǐng)吧
還有java后臺(tái)要引入相關(guān)的jar包,如下:

 
     
         org.springframework.security.oauth.boot
         spring-security-oauth2-autoconfigure
     
     
         org.springframework.security.oauth
         spring-security-oauth2
         2.3.3.RELEASE
     
     
         org.springframework.boot
         spring-boot-starter-security
     
     
         
         
     
     
         org.springframework.cloud
         spring-cloud-starter-security
     

     
         org.springframework.cloud
         spring-cloud-starter-oauth2
     
     
         org.springframework.boot
         spring-boot-starter-data-redis
     
     
         org.springframework.boot
         spring-boot-starter-jdbc
     
     
         mysql
         mysql-connector-java
     
     
         org.springframework.social
         spring-social-config
         1.1.6.RELEASE
     
     
         org.springframework.social
         spring-social-core
         1.1.6.RELEASE
     
     
         org.springframework.social
         spring-social-security
         1.1.6.RELEASE
     
     
         org.springframework.social
         spring-social-web
         1.1.6.RELEASE
     
     
         io.jsonwebtoken
         jjwt
         0.9.1
     

     
         org.apache.commons
         commons-lang3
         3.7
     

     
         org.apache.commons
         commons-collections4
         4.2
     

     
         commons-beanutils
         commons-beanutils
         1.9.3
     
     
         org.springframework.boot
         spring-boot-configuration-processor
     
     
         org.springframework.data
         spring-data-mongodb
         2.0.9.RELEASE
     
     
         org.springframework.boot
         spring-boot-starter-data-mongodb
         2.0.4.RELEASE
     
     
         com.fasterxml.jackson.core
         jackson-core
         2.9.6
     

然后在application.properties里面設(shè)置相關(guān)配置,如redis、mysql等設(shè)置,如下:

 spring.datasource.url=
 spring.datasource.username=
 spring.datasource.password=
 spring.datasource.driverClassName=com.mysql.jdbc.Driver
 
 spring.redis.host=127.0.0.1
 spring.redis.password=your_pwd
 spring.redis.port=6379
 spring.redis.timeout=30000
 
 ssb.security.social.register-url=/social/signUp
 ssb.security.social.filter-processes-url=/social-login
 ssb.security.social.bind-url=https://website/social-bind/qq
 ssb.security.social.callback-url=https://website/social-login
 ssb.security.social.connect-url=https://website/social-connect

 //QQ授權(quán)
 ssb.security.social.qq.app-id=
 ssb.security.social.qq.app-secret=
 ssb.security.social.qq.provider-id=qq
 
 //WeChat授權(quán)
 ssb.security.social.wechat.app-id=
 ssb.security.social.wechat.app-secret=
 ssb.security.social.wechat.provider-id=wechat
2、分析社交綁定ConnectController類

準(zhǔn)備工作做好之后,現(xiàn)在我們開始分析社交綁定,其實(shí)spring-social框架里已經(jīng)自帶了spring-social-web,這個(gè)jar包里面有個(gè)ConnectController.java類,這個(gè)類已經(jīng)幫我們實(shí)現(xiàn)了相關(guān)綁定與解綁實(shí)現(xiàn)方法,問題在于它是基于Session的,所以如果是前后端分離項(xiàng)目使用Session當(dāng)然應(yīng)有問題,所以我們要結(jié)合Redis來使用,把相關(guān)變量都存在Redis中,所以我們上面已經(jīng)配置好了Redis,我們?cè)賮砜纯碦edis配置代碼:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(50000);//單位為ms
        factory.setConnectTimeout(50000);//單位為ms
        return factory;
    }
}
3、獲取系統(tǒng)當(dāng)前用戶所有社交賬號(hào)綁定情況

設(shè)置好之后,我們來分析一下spring-social-web這個(gè)jar包獲取社交賬號(hào)綁定情況,它的請(qǐng)求地址是/connect,代碼如下:

@Controller
@RequestMapping({"/connect"})
public class ConnectController implements InitializingBean {
    private static final Log logger = LogFactory.getLog(ConnectController.class);
    private final ConnectionFactoryLocator connectionFactoryLocator;
    private final ConnectionRepository connectionRepository;
    private final MultiValueMap, ConnectInterceptor> connectInterceptors = new LinkedMultiValueMap();
    private final MultiValueMap, DisconnectInterceptor> disconnectInterceptors = new LinkedMultiValueMap();
    private ConnectSupport connectSupport;
    private final UrlPathHelper urlPathHelper = new UrlPathHelper();
    private String viewPath = "connect/";
    private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
    private String applicationUrl = null;
    protected static final String DUPLICATE_CONNECTION_ATTRIBUTE = "social_addConnection_duplicate";
    protected static final String PROVIDER_ERROR_ATTRIBUTE = "social_provider_error";
    protected static final String AUTHORIZATION_ERROR_ATTRIBUTE = "social_authorization_error";

    @Inject
    public ConnectController(ConnectionFactoryLocator connectionFactoryLocator, ConnectionRepository connectionRepository) {
        this.connectionFactoryLocator = connectionFactoryLocator;
        this.connectionRepository = connectionRepository;
    }

    /** @deprecated */
    @Deprecated
    public void setInterceptors(List> interceptors) {
        this.setConnectInterceptors(interceptors);
    }

    public void setConnectInterceptors(List> interceptors) {
        Iterator var2 = interceptors.iterator();

        while(var2.hasNext()) {
            ConnectInterceptor interceptor = (ConnectInterceptor)var2.next();
            this.addInterceptor(interceptor);
        }

    }

    public void setDisconnectInterceptors(List> interceptors) {
        Iterator var2 = interceptors.iterator();

        while(var2.hasNext()) {
            DisconnectInterceptor interceptor = (DisconnectInterceptor)var2.next();
            this.addDisconnectInterceptor(interceptor);
        }

    }

    public void setApplicationUrl(String applicationUrl) {
        this.applicationUrl = applicationUrl;
    }

    public void setViewPath(String viewPath) {
        this.viewPath = viewPath;
    }

    public void setSessionStrategy(SessionStrategy sessionStrategy) {
        this.sessionStrategy = sessionStrategy;
    }

    public void addInterceptor(ConnectInterceptor interceptor) {
        Class serviceApiType = GenericTypeResolver.resolveTypeArgument(interceptor.getClass(), ConnectInterceptor.class);
        this.connectInterceptors.add(serviceApiType, interceptor);
    }

    public void addDisconnectInterceptor(DisconnectInterceptor interceptor) {
        Class serviceApiType = GenericTypeResolver.resolveTypeArgument(interceptor.getClass(), DisconnectInterceptor.class);
        this.disconnectInterceptors.add(serviceApiType, interceptor);
    }

    @RequestMapping(
        method = {RequestMethod.GET}
    )
    public String connectionStatus(NativeWebRequest request, Model model) {
        this.setNoCache(request);
        this.processFlash(request, model);
        Map>> connections = this.connectionRepository.findAllConnections();
        model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());
        model.addAttribute("connectionMap", connections);
        return this.connectView();
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET}
    )
    public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {
        this.setNoCache(request);
        this.processFlash(request, model);
        List> connections = this.connectionRepository.findConnections(providerId);
        this.setNoCache(request);
        if(connections.isEmpty()) {
            return this.connectView(providerId);
        } else {
            model.addAttribute("connections", connections);
            return this.connectedView(providerId);
        }
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.POST}
    )
    public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        MultiValueMap parameters = new LinkedMultiValueMap();
        this.preConnect(connectionFactory, parameters, request);

        try {
            return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
        } catch (Exception var6) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var6);
            return this.connectionStatusRedirect(providerId, request);
        }
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET},
        params = {"oauth_token"}
    )
    public RedirectView oauth1Callback(@PathVariable String providerId, NativeWebRequest request) {
        try {
            OAuth1ConnectionFactory connectionFactory = (OAuth1ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);
            Connection connection = this.connectSupport.completeConnection(connectionFactory, request);
            this.addConnection(connection, connectionFactory, request);
        } catch (Exception var5) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var5);
            logger.warn("Exception while handling OAuth1 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");
        }

        return this.connectionStatusRedirect(providerId, request);
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET},
        params = {"code"}
    )
    public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {
        try {
            OAuth2ConnectionFactory connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);
            Connection connection = this.connectSupport.completeConnection(connectionFactory, request);
            this.addConnection(connection, connectionFactory, request);
        } catch (Exception var5) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var5);
            logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");
        }

        return this.connectionStatusRedirect(providerId, request);
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.GET},
        params = {"error"}
    )
    public RedirectView oauth2ErrorCallback(@PathVariable String providerId, @RequestParam("error") String error, @RequestParam(value = "error_description",required = false) String errorDescription, @RequestParam(value = "error_uri",required = false) String errorUri, NativeWebRequest request) {
        Map errorMap = new HashMap();
        errorMap.put("error", error);
        if(errorDescription != null) {
            errorMap.put("errorDescription", errorDescription);
        }

        if(errorUri != null) {
            errorMap.put("errorUri", errorUri);
        }

        this.sessionStrategy.setAttribute(request, "social_authorization_error", errorMap);
        return this.connectionStatusRedirect(providerId, request);
    }

    @RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.DELETE}
    )
    public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        this.preDisconnect(connectionFactory, request);
        this.connectionRepository.removeConnections(providerId);
        this.postDisconnect(connectionFactory, request);
        return this.connectionStatusRedirect(providerId, request);
    }

    @RequestMapping(
        value = {"/{providerId}/{providerUserId}"},
        method = {RequestMethod.DELETE}
    )
    public RedirectView removeConnection(@PathVariable String providerId, @PathVariable String providerUserId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        this.preDisconnect(connectionFactory, request);
        this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));
        this.postDisconnect(connectionFactory, request);
        return this.connectionStatusRedirect(providerId, request);
    }

    protected String connectView() {
        return this.getViewPath() + "status";
    }

    protected String connectView(String providerId) {
        return this.getViewPath() + providerId + "Connect";
    }

    protected String connectedView(String providerId) {
        return this.getViewPath() + providerId + "Connected";
    }

    protected RedirectView connectionStatusRedirect(String providerId, NativeWebRequest request) {
        HttpServletRequest servletRequest = (HttpServletRequest)request.getNativeRequest(HttpServletRequest.class);
        String path = "/connect/" + providerId + this.getPathExtension(servletRequest);
        if(this.prependServletPath(servletRequest)) {
            path = servletRequest.getServletPath() + path;
        }

        return new RedirectView(path, true);
    }

    public void afterPropertiesSet() throws Exception {
        this.connectSupport = new ConnectSupport(this.sessionStrategy);
        if(this.applicationUrl != null) {
            this.connectSupport.setApplicationUrl(this.applicationUrl);
        }

    }

    private boolean prependServletPath(HttpServletRequest request) {
        return !this.urlPathHelper.getPathWithinServletMapping(request).equals("");
    }

    private String getPathExtension(HttpServletRequest request) {
        String fileName = this.extractFullFilenameFromUrlPath(request.getRequestURI());
        String extension = StringUtils.getFilenameExtension(fileName);
        return extension != null?"." + extension:"";
    }

    private String extractFullFilenameFromUrlPath(String urlPath) {
        int end = urlPath.indexOf(63);
        if(end == -1) {
            end = urlPath.indexOf(35);
            if(end == -1) {
                end = urlPath.length();
            }
        }

        int begin = urlPath.lastIndexOf(47, end) + 1;
        int paramIndex = urlPath.indexOf(59, begin);
        end = paramIndex != -1 && paramIndex < end?paramIndex:end;
        return urlPath.substring(begin, end);
    }

    private String getViewPath() {
        return this.viewPath;
    }

    private void addConnection(Connection connection, ConnectionFactory connectionFactory, WebRequest request) {
        try {
            this.connectionRepository.addConnection(connection);
            this.postConnect(connectionFactory, connection, request);
        } catch (DuplicateConnectionException var5) {
            this.sessionStrategy.setAttribute(request, "social_addConnection_duplicate", var5);
        }

    }

    private void preConnect(ConnectionFactory connectionFactory, MultiValueMap parameters, WebRequest request) {
        Iterator var4 = this.interceptingConnectionsTo(connectionFactory).iterator();

        while(var4.hasNext()) {
            ConnectInterceptor interceptor = (ConnectInterceptor)var4.next();
            interceptor.preConnect(connectionFactory, parameters, request);
        }

    }

    private void postConnect(ConnectionFactory connectionFactory, Connection connection, WebRequest request) {
        Iterator var4 = this.interceptingConnectionsTo(connectionFactory).iterator();

        while(var4.hasNext()) {
            ConnectInterceptor interceptor = (ConnectInterceptor)var4.next();
            interceptor.postConnect(connection, request);
        }

    }

    private void preDisconnect(ConnectionFactory connectionFactory, WebRequest request) {
        Iterator var3 = this.interceptingDisconnectionsTo(connectionFactory).iterator();

        while(var3.hasNext()) {
            DisconnectInterceptor interceptor = (DisconnectInterceptor)var3.next();
            interceptor.preDisconnect(connectionFactory, request);
        }

    }

    private void postDisconnect(ConnectionFactory connectionFactory, WebRequest request) {
        Iterator var3 = this.interceptingDisconnectionsTo(connectionFactory).iterator();

        while(var3.hasNext()) {
            DisconnectInterceptor interceptor = (DisconnectInterceptor)var3.next();
            interceptor.postDisconnect(connectionFactory, request);
        }

    }

    private List> interceptingConnectionsTo(ConnectionFactory connectionFactory) {
        Class serviceType = GenericTypeResolver.resolveTypeArgument(connectionFactory.getClass(), ConnectionFactory.class);
        List> typedInterceptors = (List)this.connectInterceptors.get(serviceType);
        if(typedInterceptors == null) {
            typedInterceptors = Collections.emptyList();
        }

        return typedInterceptors;
    }

    private List> interceptingDisconnectionsTo(ConnectionFactory connectionFactory) {
        Class serviceType = GenericTypeResolver.resolveTypeArgument(connectionFactory.getClass(), ConnectionFactory.class);
        List> typedInterceptors = (List)this.disconnectInterceptors.get(serviceType);
        if(typedInterceptors == null) {
            typedInterceptors = Collections.emptyList();
        }

        return typedInterceptors;
    }

    private void processFlash(WebRequest request, Model model) {
        this.convertSessionAttributeToModelAttribute("social_addConnection_duplicate", request, model);
        this.convertSessionAttributeToModelAttribute("social_provider_error", request, model);
        model.addAttribute("social_authorization_error", this.sessionStrategy.getAttribute(request, "social_authorization_error"));
        this.sessionStrategy.removeAttribute(request, "social_authorization_error");
    }

    private void convertSessionAttributeToModelAttribute(String attributeName, WebRequest request, Model model) {
        if(this.sessionStrategy.getAttribute(request, attributeName) != null) {
            model.addAttribute(attributeName, Boolean.TRUE);
            this.sessionStrategy.removeAttribute(request, attributeName);
        }

    }

    private void setNoCache(NativeWebRequest request) {
        HttpServletResponse response = (HttpServletResponse)request.getNativeResponse(HttpServletResponse.class);
        if(response != null) {
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 1L);
            response.setHeader("Cache-Control", "no-cache");
            response.addHeader("Cache-Control", "no-store");
        }

    }
}

上面就是ConnectController的源碼了,我們現(xiàn)在分析一下獲取當(dāng)前用戶社交綁定情況的方法:

      @RequestMapping(
      method = {RequestMethod.GET}
  )
  public String connectionStatus(NativeWebRequest request, Model model) {
      this.setNoCache(request);
      this.processFlash(request, model);
      Map>> connections = this.connectionRepository.findAllConnections();
      model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());
      model.addAttribute("connectionMap", connections);
      return this.connectView();
  }

  @RequestMapping(
      value = {"/{providerId}"},
      method = {RequestMethod.GET}
  )
  public String connectionStatus(@PathVariable String providerId, NativeWebRequest request, Model model) {
      this.setNoCache(request);
      this.processFlash(request, model);
      List> connections = this.connectionRepository.findConnections(providerId);
      this.setNoCache(request);
      if(connections.isEmpty()) {
          return this.connectView(providerId);
      } else {
          model.addAttribute("connections", connections);
          return this.connectedView(providerId);
      }
  }

對(duì)了,就是這兩個(gè)方法,前面第一個(gè)方法請(qǐng)求的地址是:/connect(需要用戶登錄) 這個(gè)地址是獲取當(dāng)前用戶所有社交賬號(hào)綁定情況,第二個(gè)方法請(qǐng)求的地址是:/connect/{providerId}(需要用戶登錄) 這個(gè)地址是獲取某個(gè)社交賬號(hào)綁定情況,如/connect/qq,所以我們要獲取當(dāng)前用戶綁定的所有社交賬號(hào)綁定情況,使用的是第一個(gè)方法,但是現(xiàn)在有個(gè)問題,獲取完之后 它是直接跳轉(zhuǎn)頁面到/connect/status,當(dāng)然這不是我們想要的,我們要修改這個(gè)類,比如地址換成/socialConnect,這個(gè)換成自己的就好,然后我們來改下這個(gè)方法,如下:

 @RequestMapping(
         method = {RequestMethod.GET}
 )
 public ResponseEntity connectionStatus(NativeWebRequest request, Model model) throws JsonProcessingException {
     this.setNoCache(request);
     this.processFlash(request, model);
     Map>> connections = this.connectionRepository.findAllConnections();
     model.addAttribute("providerIds", this.connectionFactoryLocator.registeredProviderIds());
     model.addAttribute("connectionMap", connections);
     Map result = new HashMap();
     for (String key : connections.keySet()){
         result.put(key, org.apache.commons.collections.CollectionUtils.isNotEmpty(connections.get(key)));
     }
     return ResponseEntity.ok(objectMapper.writeValueAsString(result));
 }

改好的代碼直接返回Json數(shù)據(jù)給前端,而不是跳轉(zhuǎn)頁面,完美解決了前后端分離項(xiàng)目問題,好了,我們使用postman發(fā)送請(qǐng)求測(cè)試看看:

如圖所示,我們成功獲取當(dāng)前登錄用戶所有社交賬號(hào)綁定情況了(為什么這里只有qq和微信?社交賬號(hào)的類型是你application.proterties里面配置的)。

4、綁定社交賬號(hào)

好了,我們來看看綁定社交賬號(hào)的方法:

  @RequestMapping(
      value = {"/{providerId}"},
      method = {RequestMethod.POST}
  )
  public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
      ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
      MultiValueMap parameters = new LinkedMultiValueMap();
      this.preConnect(connectionFactory, parameters, request);

      try {
          return new RedirectView(this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
      } catch (Exception var6) {
          this.sessionStrategy.setAttribute(request, "social_provider_error", var6);
          return this.connectionStatusRedirect(providerId, request);
      }
  }
  
  @RequestMapping(
      value = {"/{providerId}"},
      method = {RequestMethod.GET},
      params = {"code"}
  )
  public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {
      try {
          OAuth2ConnectionFactory connectionFactory = (OAuth2ConnectionFactory)this.connectionFactoryLocator.getConnectionFactory(providerId);
          Connection connection = this.connectSupport.completeConnection(connectionFactory, request);
          this.addConnection(connection, connectionFactory, request);
      } catch (Exception var5) {
          this.sessionStrategy.setAttribute(request, "social_provider_error", var5);
          logger.warn("Exception while handling OAuth2 callback (" + var5.getMessage() + "). Redirecting to " + providerId + " connection status page.");
      }

      return this.connectionStatusRedirect(providerId, request);
  }

現(xiàn)在來分析 下這兩個(gè) 方法的作用,第一個(gè)方法請(qǐng)求的地址是:POST /connect/{providerId}(需要登錄) ,第二個(gè)方法請(qǐng)求地址是:GET /connect/{providerId}?code=&state=(需要登錄),第一個(gè)方法是獲取社交授權(quán)連接地址(這個(gè)是你自己社交登錄時(shí)候封裝好的,這里我不打算詳細(xì)講解,后面課程再放出來吧)比如qq的授權(quán)地址:https://graph.qq.com/oauth2.0...,這樣當(dāng)你授權(quán)成功之后就回調(diào)到了第二個(gè)方法里面,順便把code和state原樣返回過來,這一套綁定機(jī)制都是基于session的,下面我們來分析看下他是如何實(shí)現(xiàn)的。

我們以微信為例,首先我們發(fā)送一個(gè)POST請(qǐng)求/connect/wechat,因?yàn)槟阋呀?jīng)登錄了,所以后臺(tái)可以獲取當(dāng)前user是誰,然后就獲取到請(qǐng)求的鏈接:https://open.weixin.qq.com/co...,最后就是跳轉(zhuǎn)到這個(gè)鏈接上面去。這是第一個(gè)方法的作用,接下來我們分析第二個(gè)方法。

請(qǐng)求上面的鏈接之后就是跳轉(zhuǎn)到微信掃碼的頁面,如下所示:

掃完之后立馬就跳到上面鏈接redirect_uri地址上面去,也就是現(xiàn)在的第二個(gè)方法上面,而且是帶著state和code兩個(gè)參數(shù),這時(shí)候后臺(tái)開始驗(yàn)證你回傳過來的state值是不是匹配的,不匹配就報(bào)錯(cuò)并且跳轉(zhuǎn)到出錯(cuò)頁面,匹配的話就往下走,并且通過code獲取SpringSecurity OAuth相關(guān)社交用戶信息并保存到數(shù)據(jù)庫中,這就是code和state的作用,驗(yàn)證和獲取完之后就可以,這樣你就綁定成功了,最后跳轉(zhuǎn)到/connected/wechat頁面了,這樣就結(jié)束了綁定功能了。

那么我們前后端分離項(xiàng)目要使用這套機(jī)制,我們必須改一下他的源碼了。

首先第一個(gè)方法,我們要把userId保存到以state的redis鍵值對(duì)中,也就是:{state:userId},然后以JSON的格式返回社交授權(quán)的鏈接給前臺(tái),這是第一個(gè)方法要修改的思路。

然后第二個(gè)方法,是社交授權(quán)鏈接返回回來的,因?yàn)榍昂蠖朔蛛x項(xiàng)目session就無法使用了,所以要獲取用戶信息必須通過上面redis保存的{state:userId},來獲取用戶id。再一個(gè)我們通過code獲取社交用戶信息,兩個(gè)數(shù)據(jù)都獲取了,這個(gè)時(shí)候我們就可以安心的把社交用戶信息保存到數(shù)據(jù)庫中(這里的通過state從redis中獲取userId,其實(shí)也是一種驗(yàn)證state的方式,你想想可是呢!),最后就跳轉(zhuǎn)到你想要的頁面就好了,下面就是修改后的代碼了,可以看看:

@RequestMapping(
            value = {"/{providerId}"},
            method = {RequestMethod.POST}
    )
    public ResponseEntity connect(@PathVariable String providerId,
                                     NativeWebRequest request) {
        HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);
        Principal user = nativeRequest.getUserPrincipal();
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        MultiValueMap parameters = new LinkedMultiValueMap();
        this.preConnect(connectionFactory, parameters, request);
        try {
            String social_connect_url = this.connectSupport.buildOAuthUrl(connectionFactory, request, parameters);
            String state = (String) this.sessionStrategy.getAttribute(request, "oauth2State");
            this.sessionStrategy.removeAttribute(request, "oauth2State");
            //把userId以state為key的形式保存到redis中
            socialRedisHelper.saveStateUserId(state, user.getName());
            //返回社交鏈接地址
            return ResponseEntity.ok(social_connect_url);
        } catch (Exception var6) {
            this.sessionStrategy.setAttribute(request, "social_provider_error", var6);
            logger.info(var6.getMessage());
            return null;
        }
    }
    
    //輔助方法1
    protected String callbackUrl(NativeWebRequest request) {
        if (this.callbackUrl != null) {
            return this.callbackUrl;
        } else {
            HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class);
            return this.applicationUrl != null ? this.applicationUrl + this.connectPath(nativeRequest) : nativeRequest.getRequestURL().toString();
        }
    }

    //輔助方法2
    private String connectPath(HttpServletRequest request) {
        String pathInfo = request.getPathInfo();
        return request.getServletPath() + (pathInfo != null ? pathInfo : "");
    }

    //回調(diào)方法
    @RequestMapping(
            value = {"/{providerId}"},
            method = {RequestMethod.GET},
            params = {"code"}
    )
    public void oauth2Callback(@PathVariable String providerId,
                               NativeWebRequest request,
                               HttpServletResponse response) {
        try {
            //ConnectController是先保存在session里面,然后回調(diào)從session里面取出來校驗(yàn)
            //我現(xiàn)在是通過redis保存state 的 userId,這樣就相當(dāng)于校驗(yàn)了state
            String state = request.getParameter("state");
            String code = request.getParameter("code");
            OAuth2ConnectionFactory connectionFactory = (OAuth2ConnectionFactory) this.connectionFactoryLocator.getConnectionFactory(providerId);
            AccessGrant accessGrant = connectionFactory.getOAuthOperations().exchangeForAccess(code, this.callbackUrl(request), null);
            Connection connection = connectionFactory.createConnection(accessGrant);
            //從redis中獲取userid
            String userId = socialRedisHelper.getStateUserId(state);
            //保存到數(shù)據(jù)庫中
            jdbcConnectionRepository.createConnectionRepository(userId).addConnection(connection);
            //跳轉(zhuǎn)頁面到前臺(tái)任何你想設(shè)置的地址
            response.sendRedirect(connectUrl);
        } catch (Exception ex) {
            logger.info(ex.getMessage());
        }
    }

這樣你就完成了后臺(tái)綁定相關(guān)工作,那么我把前端相關(guān)代碼也放出來大家看下吧:

gotoBind(type){
            let url = `${this.$url}/socialConnect/${type}`;
            this.$post(url)
            .then(res=>{
                if(res.code == 0){
                    this.openWindow(res.data.redirect_uri)
                }
            })
        },
openWindow(url){
            let sf_H = 550;
            let sf_W = 720;
            var iTop = (window.screen.height-30 -sf_H)/2; //獲得窗口的垂直位置;  
            var iLeft = (window.screen.width-10 -sf_W)/2; //獲得窗口的水平位置;
            let s = window.open(url,"social_bind_form","height="+sf_H+
            ", width="+sf_W+",top="+iTop+",left="+iLeft+"toolbar=no, menubar=no, scrollbars=no, status=no, location=yes, resizable=yes");
        },

上面是獲取社交綁定地址并跳轉(zhuǎn),下面是回調(diào)成功之后關(guān)閉對(duì)話框并刷新的頁面代碼。



我們來演示一下:

5、解綁社交賬號(hào)

綁定社交賬號(hào)已經(jīng)成功了,現(xiàn)在我們來看一下如何解綁社交賬號(hào)吧,我們先看下源碼是如何實(shí)現(xiàn)的,如下

@RequestMapping(
        value = {"/{providerId}"},
        method = {RequestMethod.DELETE}
    )
    public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        this.preDisconnect(connectionFactory, request);
        this.connectionRepository.removeConnections(providerId);
        this.postDisconnect(connectionFactory, request);
        return this.connectionStatusRedirect(providerId, request);
    }

    @RequestMapping(
        value = {"/{providerId}/{providerUserId}"},
        method = {RequestMethod.DELETE}
    )
    public RedirectView removeConnection(@PathVariable String providerId, @PathVariable String providerUserId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        this.preDisconnect(connectionFactory, request);
        this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));
        this.postDisconnect(connectionFactory, request);
        return this.connectionStatusRedirect(providerId, request);
    }

第一個(gè)方法請(qǐng)求地址是:Delete /connect/{providerId}(需登錄),第二個(gè)方法請(qǐng)求地址是:Delete /connect/{providerId}/{providerUserId}(需登錄),注意這里的providerUserId其實(shí)就是社交用戶id,比如微信的openId,第一個(gè)方法是根據(jù)登錄的userId和providerId來刪除數(shù)據(jù)庫中綁定的社交用戶數(shù)據(jù),第二個(gè)方法是根據(jù)登錄的userId和providerId還有providerUserId來刪除數(shù)據(jù)庫中綁定的社交用戶數(shù)據(jù),這兩個(gè) 方法都有相同的一點(diǎn)就是跳轉(zhuǎn)到刪除之后的頁面,所以我們只要把跳轉(zhuǎn)頁面以JSON的形式返回給前端就好,下面就是修改后的代碼:

 @RequestMapping(
            value = {"/{providerId}"},
            method = {RequestMethod.DELETE}
    )
    public ResponseEntity removeConnections(@PathVariable String providerId, NativeWebRequest request) {
        ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
        this.preDisconnect(connectionFactory, request);
        this.connectionRepository.removeConnections(providerId);
        this.postDisconnect(connectionFactory, request);
        return ResponseEntity.ok("success");
    }

    @RequestMapping(
            value = {"/{providerId}/{providerUserId}"},
            method = {RequestMethod.DELETE}
    )
    public ResponseEntity removeConnection(@PathVariable String providerId,
                                              @PathVariable String providerUserId,
                                              NativeWebRequest request) throws IOException {
        try {
            ConnectionFactory connectionFactory = this.connectionFactoryLocator.getConnectionFactory(providerId);
            this.preDisconnect(connectionFactory, request);
            this.connectionRepository.removeConnection(new ConnectionKey(providerId, providerUserId));
            this.postDisconnect(connectionFactory, request);
        } catch (Exception ex) {
            logger.info(ex.getMessage());
        }
        return ResponseEntity.ok("success");
    }

我們?cè)侔亚岸舜a貼出來:

gotoUnBind(type){
            let url = `${this.$url}/socialConnect/${type}`;
            this.$delete(url)
            .then(res=>{
                if(res.code == 0){
                    this.$Message.success("解綁成功!")
                    location.reload();
                }
            })
        },
6、總結(jié):

1、只要把思路理清楚了,其實(shí)修改成自己想要的代碼就不難
2、注意ConnectController代碼是基于Session的,所以你必須要登錄的情況下才能使用
3、redis的使用在這里發(fā)揮到了一定作用,所以說前后端分離項(xiàng)目離不開redis

7、引用

qq互聯(lián)文檔
Spring Security Oauth2.0 實(shí)現(xiàn)短信驗(yàn)證碼登錄
深入了解 Spring Security
SpringBoot+Spring Security基本配置
spring-social-mongodb
微信的redirect_uri參數(shù)錯(cuò)誤解決辦法
網(wǎng)頁微信第三方登錄-redirect_uri參數(shù)錯(cuò)誤
Java實(shí)現(xiàn)QQ、微信、新浪微博第三方登錄
如何從零開始對(duì)接第三方登錄(Java版):QQ登錄和微博登錄
QQ授權(quán)登錄改
Spring Security QQ 登陸
第三方APP實(shí)現(xiàn)QQ登陸
2 Apache Shiro 身份認(rèn)證(登錄)
Spring Security 實(shí)戰(zhàn):QQ登錄實(shí)現(xiàn)
基于Spring的QQ第三方登錄實(shí)現(xiàn)
Spring Security源碼分析三:Spring Social實(shí)現(xiàn)QQ社交登錄
微信授權(quán)登錄-前后端分離
從零開始的Spring Security Oauth2(一)
Spring Boot and OAuth2
Spring security OAuth2 深入解析
spring boot 入門之security oauth2 jwt完美整合例子-java編程
jojozhai/security
window.open打開的窗口關(guān)閉后刷新父頁面的子頁面
Spring Security 實(shí)戰(zhàn):QQ登錄實(shí)現(xiàn)jwt

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/77320.html

相關(guān)文章

  • 海航生態(tài)科技輿情大數(shù)據(jù)平臺(tái)容器化改造

    摘要:本文轉(zhuǎn)載自微信公眾號(hào)賬號(hào),作者為海航生態(tài)科技技術(shù)研究院大數(shù)據(jù)開發(fā)工程師高顏。文章介紹了海航生態(tài)科技輿情大數(shù)據(jù)平臺(tái)的容器化改造經(jīng)驗(yàn),包括初期技術(shù)架構(gòu)應(yīng)用容器化架構(gòu)遷移持續(xù)發(fā)布與部署。 本文轉(zhuǎn)載自微信公眾號(hào)Docker(賬號(hào):dockerone),作者為海航生態(tài)科技技術(shù)研究院大數(shù)據(jù)開發(fā)工程師高顏。 文章介紹了海航生態(tài)科技輿情大數(shù)據(jù)平臺(tái)的容器化改造經(jīng)驗(yàn),包括初期技術(shù)架構(gòu)、應(yīng)用容器化、架構(gòu)遷...

    idealcn 評(píng)論0 收藏0
  • Deeplink(深度鏈接)如何提高App轉(zhuǎn)化率、留存率

    摘要:升級(jí)版延遲深度鏈接技術(shù)相比,增加了一個(gè)判斷,能在用戶點(diǎn)擊鏈接時(shí)判斷設(shè)備是否安裝了目標(biāo),如果沒有安裝,則跳轉(zhuǎn)應(yīng)用市場(chǎng)或者瀏覽器中引導(dǎo)下載,用戶安裝后再次實(shí)現(xiàn)的場(chǎng)景還原功能。 移動(dòng)互聯(lián)網(wǎng)時(shí)代,信息的分享傳播無疑是 App 引流增長(zhǎng)的關(guān)鍵,與其花費(fèi)大量精力和成本找渠道、硬推廣,不如從細(xì)節(jié)下手,用最快最簡(jiǎn)便的方法實(shí)現(xiàn) Deeplink(深度鏈接)技術(shù),打破信息孤島、縮短分享路徑、優(yōu)化用戶體驗(yàn),...

    darryrzhong 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<