spring-security-oauth2(十二 ) 註冊邏輯

爲何跳轉signup

在上一章結尾中,掃碼登錄後就會調到就跳到signup註冊頁面,這是怎麼回事呢?我們來跟下源碼。

org.springframework.social.security.SocialAuthenticationProvider#authenticate 

org.springframework.social.security.SocialAuthenticationFilter#doAuthentication

默認註冊界面

 分析可知:用戶首次進行社交登錄,社交用戶和系統用戶還沒有進行綁定,所有會調到social默認的註冊界面signup ,但是這個路徑我們在瀏覽器權限配置中沒有放行,所以纔會又跳到我們的認證地址。

修改默認註冊界面

就是把默認的/signup路徑修改成我們自己的路徑,這個註冊界面通常應該是在我們的demo項目中處理

主要添加這一行

在權限配置類中要放行這個路徑:

在配置文件類中添加註冊路徑

 配置文件中添加demo-signup.html

 demo註冊界面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo註冊界面</title>
</head>
<body>
<h2>demo註冊界面</h2>
<h3>表單註冊</h3>
<form action="/user/regist" method="post">
    <table>
        <tr>
            <td>用戶名:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密碼:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td colspan="2">
                <button type="submit" value="bind">綁定</button>
                <button type="submit" value="regist">註冊</button>
            </td>
        </tr>
    </table>
</form>
</body>
</html>

ok 我們來測試下 看是否可以跳到我們的自定義界面

註冊:社交用戶首次登陸,系統中還沒有用戶信息。

綁定:將社交信息和系統中已有的用戶信息進行綁定。

 social與註冊互動

1.通常在註冊或綁定邏輯執行之前(/uesr/regist),註冊界面會顯示一些社交用戶信息如暱稱圖像並提示用戶進行註冊動作,怎麼拿到用戶信息呢。

2.還有怎麼將系統用戶信息和social互動?也就是要把關聯信息插入到表UserConnection中

關鍵工具:org.springframework.social.connect.web.ProviderSignInUtils  

配置 ProviderSignInUtils 

com.rui.tiger.auth.core.social.SocialConfig#providerSignInUtils

/**
 * social和註冊互動工具類
 * @return
 */
@Bean
public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator connectionFactoryLocator){
    return new ProviderSignInUtils(connectionFactoryLocator,getUsersConnectionRepository(connectionFactoryLocator));
}
SocialUserInfo 定義社交用戶信息,可以給前臺註冊界面進行展示
package com.rui.tiger.auth.browser.support;

import lombok.Data;

/**
 * 社交用戶信息封裝
 * @author CaiRui
 * @date 2019-01-09 18:24
 */
@Data
public class SocialUserInfo {

	private String providerId;//供應商ID

	private String providerUserId;//供應商用戶ID 即openID

	private String nickName;//暱稱

	private String headImg;//圖像地址

}
BrowserRequireController 瀏覽器控制類新增獲取社交用戶信息
package com.rui.tiger.auth.browser.controller;

import com.rui.tiger.auth.browser.support.SocialUserInfo;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import com.rui.tiger.auth.core.support.SimpleResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 用戶登錄認證控制器
 *
 * @author CaiRui
 * @date 2018-12-5 12:44
 */
@RestController
@Slf4j
public class BrowserRequireController {

	//封裝了引發跳轉請求的工具類  https://blog.csdn.net/honghailiang888/article/details/53671108
	private RequestCache requestCache = new HttpSessionRequestCache();
	// spring的工具類:封裝了所有跳轉行爲策略類
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	@Autowired
	private SecurityProperties securityProperties;

	private static final String HTML_SUFFIX = ".html";

	@Autowired
	private ProviderSignInUtils providerSignInUtils;



	/**
	 * 當需要進行身份認證的時候跳轉到此方法
	 *
	 * @param request  請求
	 * @param response 響應
	 * @return 將信息以JSON形式返回給前端
	 */
	@RequestMapping("/authentication/require")
	@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
	public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
		log.info("BrowserRequireController進來了 啦啦啦");
		// 從session緩存中獲取引發跳轉的請求
		SavedRequest savedRequest = requestCache.getRequest(request, response);
		if (null != savedRequest) {
			String redirectUrl = savedRequest.getRedirectUrl();
			log.info("引發跳轉的請求是:{}", redirectUrl);
			if (StringUtils.endsWithIgnoreCase(redirectUrl, HTML_SUFFIX)) {
				// 如果是HTML請求,那麼就直接跳轉到HTML,不再執行後面的代碼
				redirectStrategy.sendRedirect(request, response, securityProperties.getBrowser().getLoginPage());
			}
		}
		return new SimpleResponse("訪問的服務需要身份認證,請引導用戶到登錄頁面");
	}

	/**
	 * 獲取社交用戶信息  用於註冊界面顯示用戶信息
	 *
	 * @param request
	 * @return
	 */
	@GetMapping("/social/user")
	public SocialUserInfo getSocialUserInfo(HttpServletRequest request) {
		SocialUserInfo socialUserInfo = new SocialUserInfo();
		//通過工具類獲取社交用戶信息
		Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));

		socialUserInfo.setProviderId(connection.getKey().getProviderId());
		socialUserInfo.setProviderUserId(connection.getKey().getProviderUserId());
		socialUserInfo.setNickName(connection.getDisplayName());
		socialUserInfo.setHeadImg(connection.getImageUrl());
		return socialUserInfo;

	}


}

下面我們來讓註冊和social互動,註冊這個邏輯應該是在第三方系統中實現的,所以我們放在demo項目中

package com.rui.tiger.auth.demo.controller;

import com.rui.tiger.auth.demo.vo.UserVo;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.social.connect.web.ProviderSignInUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;

/**
 * 用戶控制器
 *
 * @author CaiRui
 * @date 2018-12-6 8:16
 */
@RestController
@RequestMapping("/user")
public class UserController {

	@Autowired
	private ProviderSignInUtils providerSignInUtils;

	@RequestMapping("/hello")
	public String hello() {
		return "Hello,World";
	}

	/**
	 * 獲取用戶認證信息
	 *
	 * @return
	 */
	@GetMapping("authentication")
	public Authentication getCurrentAuthentication() {
		return SecurityContextHolder.getContext().getAuthentication();
	}

	/**
	 * 獲取用戶認證信息
	 * 同getCurrentAuthentication spring 會幫我們注入
	 *
	 * @param authentication
	 * @return
	 */
	@GetMapping("authentication/auto")
	public Authentication getCurrentAuthentication2(Authentication authentication) {
		return authentication;
	}

	/**
	 * 社交註冊
	 */
	@PostMapping("/regist")
	public void regist(UserVo user, HttpServletRequest request){

		 // 不管是註冊用戶還是綁定用戶,都會拿到一個用戶唯一標識
		 String username=user.getUsername();
		//這裏處理綁定或註冊用戶邏輯

		//進行系統用戶和社交用戶入庫動作
		providerSignInUtils.doPostSignUp(username,new ServletWebRequest(request));
	}


}

別忘了對/user/regist 進行放行  com.rui.tiger.auth.browser.config.BrowserSecurityConfig#configure

ok 下面我們來測試下注冊邏輯,掃碼登錄後跳到我們的註冊界面 

點擊綁定或註冊看看,發送  post請求: /user/regist

同時我們的數據庫中也有這條記錄綁定了

這裏要注意的是這裏只是綁定了 ,並沒有進入認證。可以訪問/user/hello 試試

 要返回登錄界面再次點擊qq社交登錄,根據前面的源碼分析 由於我們的庫中已經有記錄的,不會跳到註冊界面,同時會完成認證。

ok 註冊成功完成。

 直接綁定社交賬戶

如何做到社交賬戶首次登錄,不用註冊,我們後臺直接給註冊呢? 還是來分析源碼,看看有沒有解決辦法。

org.springframework.social.connect.jdbc.JdbcUsersConnectionRepository#findUserIdsWithConnection

  所以我們要 實現這個接口 ConnectionSignUp ,放在我們的第三方系統中

package com.rui.tiger.auth.demo.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.social.connect.Connection;
import org.springframework.social.connect.ConnectionSignUp;
import org.springframework.stereotype.Component;

/**
 * 第三方應用登錄 默認註冊用戶
 * @author CaiRui
 * @date 2019-01-10 18:20
 */
@Component
@Slf4j
public class DemoConnectionSignUp implements ConnectionSignUp {

	/**
	 * 根據社交用戶信息默認創建用戶並返回用戶唯一標識
	 * @param connection
	 * @return
	 */
	@Override
	public String execute(Connection<?> connection) {
		log.info("根據社交用戶信息默認創建用戶並返回用戶唯一標識");
		//系統業務邏輯處理  目前直接以暱稱作爲唯一標識  這裏 可以調用註冊邏輯
		return connection.getDisplayName();
	}
}

com.rui.tiger.auth.core.social.SocialConfig#getUsersConnectionRepository 也同步調整

 /**
     * 業務系統用戶和服務提供商用戶對應關係,保存在表UserConnection
     * JdbcUsersConnectionRepository.sql 中有建表語句
     * userId 業務系統Id
     * providerId 服務提供商的Id
     * providerUserId  同openId
     * Encryptors  加密策略 這裏不加密
     *
     * @param connectionFactoryLocator
     * @return
     */
    @Override
    public UsersConnectionRepository getUsersConnectionRepository(ConnectionFactoryLocator connectionFactoryLocator) {
        JdbcUsersConnectionRepository jdbcUsersConnectionRepository = new JdbcUsersConnectionRepository(dataSource, connectionFactoryLocator, Encryptors.noOpText());
        //設定表UserConnection的前綴 表名不可以改變
        //jdbcUsersConnectionRepository.setTablePrefix("tiger_");
        if(connectionSignUp!=null){
            jdbcUsersConnectionRepository.setConnectionSignUp(connectionSignUp);
        }
        return jdbcUsersConnectionRepository;
    }

ok 測試之前先刪除前面註冊已經入庫的記錄

 怎麼設置登錄成功後的默認界面呢  defaultSuccessUrl("/index.html")  就是這麼簡單 跟shiro差不多

package com.rui.tiger.auth.core.config;

import com.rui.tiger.auth.core.properties.SecurityConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

/**
 * 密碼登錄的通用安全配置
 * @author CaiRui
 * @date 2018-12-26 18:11
 */
public class AbstractChannelSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private AuthenticationSuccessHandler tigerAuthenticationSuccessHandler;
	@Autowired
	private AuthenticationFailureHandler tigerAuthenticationFailureHandler;

	/**
	 * 密碼登錄配置
	 * @param http
	 * @throws Exception
	 */
	protected void 	applyPasswordAuthenticationConfig(HttpSecurity http) throws Exception {
		http.formLogin()
				.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
				.loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)//
				.defaultSuccessUrl("/index.html")
				.successHandler(tigerAuthenticationSuccessHandler)
				.failureHandler(tigerAuthenticationFailureHandler);
	}


}

ok 到此qq登錄完成 ,下一章我們將開發微信社交登錄。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章