Spring Security系列之Spring Social社交登录的绑定与解绑(十五)

在之前的Spring Social系列中,我们只是实现了使用服务提供商账号登录到业务系统中,但没有与业务系统中的账号进行关联。本章承接之前社交系列来实现社交账号与业务系统账号的绑定与解绑。

UserConnection

create table UserConnection (
	userId varchar(255) not null,
	providerId varchar(255) not null,
	providerUserId varchar(255),
	......
	primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);
复制代码

在使用社交登录的时我们创建的UserConnection表,下面我们来简单分析一下

  1. userId业务系统的用户唯一标识(我们使用的是username)
  2. providerId用于区分不同的服务提供商(qq,weixin,weibo)
  3. providerUserId 服务提供商返回的唯一标识(openid)

社交登录注册实现

取消MyConnectionSignUp

在Spring-Security源码分析六-Spring-Social社交登录源码解析中,我们得知,当配置ConnectionSignUp 时,Spring Social会根据我们配置的MyConnectionSignUp返回userId ,接着执行userDetailsService.loadUserByUserId(userId),实现社交账号登录。当取消掉MyConnectionSignUp则会抛出BadCredentialsException,BadCredentialsExceptionSocialAuthenticationFilter处理,跳转到默认的/signup注册请求,跳转之前会将当前的社交账号信息保存到session中。

添加自定义注册请求/socialRegister

@Override
    protected <T> T postProcess(T object) {
        SocialAuthenticationFilter filter = (SocialAuthenticationFilter) super.postProcess(object);
        filter.setFilterProcessesUrl(filterProcessesUrl);
        filter.setSignupUrl("/socialRegister");
        return (T) filter;
    }
复制代码

添加到.permitAll();

.authorizeRequests().antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
             	......
                "/socialRegister",//社交账号注册和绑定页面
                "/user/register",//处理社交注册请求
              	......
                .permitAll()//以上的请求都不需要认证
复制代码

配置ProviderSignInUtils

从Session中获取社交账号信息

@Bean
    public ProviderSignInUtils providerSignInUtils(ConnectionFactoryLocator factoryLocator) {
        return new ProviderSignInUtils(factoryLocator, getUsersConnectionRepository(factoryLocator));
    }
复制代码

创建SocialUserInfo

展示当前社交账号信息

@Data
public class SocialUserInfo {

	private String providerId;

	private String providerUserId;

	private String nickname;

	private String headImg;

}
复制代码

实现socialRegister和user/register

/socialRegister

@GetMapping(value = "/socialRegister")
    public ModelAndView socialRegister(HttpServletRequest request, Map<String, Object> map) {
        SocialUserInfo userInfo = new SocialUserInfo();
        Connection<?> connection = providerSignInUtils.getConnectionFromSession(new ServletWebRequest(request));
        userInfo.setProviderId(connection.getKey().getProviderId());//哪一个服务提供商
        userInfo.setProviderUserId(connection.getKey().getProviderUserId());//openid
        userInfo.setNickname(connection.getDisplayName());//名称
        userInfo.setHeadImg(connection.getImageUrl());//显示头像
        map.put("user", userInfo);
        return new ModelAndView("socialRegister", map);
    }
复制代码

/user/register

 @PostMapping("/user/register")
    public String register(SysUser user, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String userId = user.getUsername();//获取用户名
        SysUser result =  sysUserService.findByUsername(userId);//根据用户名查询用户信息
        if(result==null){
            //如果为空则注册用户
            sysUserService.save(user);
        }
        //将业务系统的用户与社交用户绑定
        providerSignInUtils.doPostSignUp(userId, new ServletWebRequest(request));
        //跳转到index
        return "redirect:/index";
    }
复制代码

修改MyUserDetailsService#loadUserByUserId

@Override
    public SocialUserDetails loadUserByUserId(String userId) throws UsernameNotFoundException {
        SysUser user = repository.findByUsername(userId);//根据用户名查找用户
        return user;
    }
复制代码

效果如下: 注册效果如下:

绑定与解绑实现

要实现绑定与解绑,首先我们需要知道社交账号的绑定状态,绑定就是重新走一下OAuth2流程,关联当前登录用户,解绑就是删除UserConnection表数据。Spring Social默认在ConnectController类上已经帮我们实现了以上的需求。

获取状态

/connect获取状态。

@RequestMapping(method=RequestMethod.GET)
	public String connectionStatus(NativeWebRequest request, Model model) {
		setNoCache(request);
		processFlash(request, model);
		Map<String, List<Connection<?>>> connections = connectionRepository.findAllConnections();//根据userId查询UserConnection表
		model.addAttribute("providerIds", connectionFactoryLocator.registeredProviderIds());//系统中已经注册的服务提供商		
		model.addAttribute("connectionMap", connections);
		return connectView();//返回connectView()
	}
	protected String connectView() {
		return getViewPath() + "status";//connect/status 
	}
复制代码

由以上可得,实现connect/status 视图即可获得社交账号的绑定状态。

实现connect/status
@Component("connect/status")
public class SocialConnectionStatusView extends AbstractView {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, List<Connection<?>>> connections = (Map<String, List<Connection<?>>>) model.get("connectionMap");

        Map<String, Boolean> result = new HashMap<>();
        for (String key : connections.keySet()) {
            result.put(key, CollectionUtils.isNotEmpty(connections.get(key)));
        }

        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(ResultUtil.success(result)));
    }
}
复制代码

返回结果如下:

绑定的实现

/connect/{providerId}绑定社交账号(POST请求)

跳转到授权的页面
@RequestMapping(value="/{providerId}", method=RequestMethod.POST)
	public RedirectView connect(@PathVariable String providerId, NativeWebRequest request) {
		ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);
		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<String, String>(); 
		preConnect(connectionFactory, parameters, request);
		try {
			return new RedirectView(connectSupport.buildOAuthUrl(connectionFactory, request, parameters));
		} catch (Exception e) {
			sessionStrategy.setAttribute(request, PROVIDER_ERROR_ATTRIBUTE, e);
			return connectionStatusRedirect(providerId, request);
		}
	}
复制代码

授权成功的回调地址

//将当前的登录账户与社交账号绑定(写入到UserConnection表)
@RequestMapping(value="/{providerId}", method=RequestMethod.GET, params="code")
	public RedirectView oauth2Callback(@PathVariable String providerId, NativeWebRequest request) {
		try {
			OAuth2ConnectionFactory<?> connectionFactory = (OAuth2ConnectionFactory<?>) connectionFactoryLocator.getConnectionFactory(providerId);
			Connection<?> connection = connectSupport.completeConnection(connectionFactory, request);
			addConnection(connection, connectionFactory, request);
		} catch (Exception e) {
			sessionStrategy.setAttribute(request, PROVIDER_ERROR_ATTRIBUTE, e);
			logger.warn("Exception while handling OAuth2 callback (" + e.getMessage() + "). Redirecting to " + providerId +" connection status page.");
		}
		return connectionStatusRedirect(providerId, request);
	}
	
	//返回/connext/qqed视图
	protected RedirectView connectionStatusRedirect(String providerId, NativeWebRequest request) {
		HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
		String path = "/connect/" + providerId + getPathExtension(servletRequest);
		if (prependServletPath(servletRequest)) {
			path = servletRequest.getServletPath() + path;
		}
		return new RedirectView(path, true);
	}
复制代码
实现 connect/qqConnected视图
@Bean("connect/qqConnected")
    public View qqConnectedView() {
        return new SocialConnectView();
    }
	
	public class SocialConnectView extends AbstractView {
    @Override
    protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String msg = "";
        response.setContentType("text/html;charset=UTF-8");
        if (model.get("connections") == null) {
            msg = "unBindingSuccess";
//            response.getWriter().write("<h3>解绑成功</h3>");
        } else {
            msg = "bindingSuccess";
//            response.getWriter().write("<h3>绑定成功</h3>");
        }

        response.sendRedirect("/message/" + msg);
    }
}
复制代码

效果如下:

解绑的实现

/connect/{providerId}绑定社交账号(DELETE请求

//删除UserConnection表数据,返回connect/qqConnect视图
@RequestMapping(value="/{providerId}", method=RequestMethod.DELETE)
	public RedirectView removeConnections(@PathVariable String providerId, NativeWebRequest request) {
		ConnectionFactory<?> connectionFactory = connectionFactoryLocator.getConnectionFactory(providerId);
		preDisconnect(connectionFactory, request);
		connectionRepository.removeConnections(providerId);
		postDisconnect(connectionFactory, request);
		return connectionStatusRedirect(providerId, request);
	}
复制代码
实现connect/qqConnect视图
/**
     * /connect/qq POST请求,绑定微信返回connect/qqConnected视图
     * /connect/qq DELETE请求,解绑返回connect/qqConnect视图
     * @return
     */
    @Bean({"connect/qqConnect", "connect/qqConnected"})
    @ConditionalOnMissingBean(name = "qqConnectedView")
    public View qqConnectedView() {
        return new SocialConnectView();
    }
复制代码

效果如下:

代码下载

从我的 github 中下载,github.com/longfeizhen…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: DPDK是一种开源的数据包处理框架,它能够大大提升数据包处理的速度以及效率。DPDK的性能在很大程度上依赖于它与网络接口卡的配合。为了最大化性能,需要将网络接口卡绑定到DPDK上。 DPDK在绑定网卡时,需要先在Linux系统中安装igb_uio驱动程序。然后,可以使用DPDK自带的绑定脚本进行绑定,如下所示: ./usertools/dpdk-devbind.py --bind=igb_uio eth0 其中,“eth0”为需要绑定的网卡名。绑定完成后,可以使用DPDK提供的性能测试工具来测试性能。 需要注意的是,在绑定之前,应先停止任何已经在使用该网卡的服务或应用程序。在绑定过程中,还要确保正确配置DPDK环境变量,如DPDK_SDK和RTE_SDK等。此外,在绑定完成后还需要检查网卡是否成功被绑定。 如果需要解绑,可以使用同样的绑定脚本来进行操作,只需要将命令中的“bind”替换为“unbind”,如下所示: ./usertools/dpdk-devbind.py --unbind eth0 绑定解绑过程需要小心谨慎,否则可能会导致系统崩溃或者网络故障。因此,在操作前应先确认自己掌握了足够的知识和经验,才能进行相应的操作。 ### 回答2: DPDK是一种高性能数据平面开发工具集,可以用于加速包处理和数据包转发。绑定解绑网络接口是DPDK的重要功能,它可以为应用程序提供更高效的包处理和网络传输能力。 网卡绑定是将DPDK与物理网卡连接起来的过程。通常情况下,DPDK使用IGB UIO模块将物理网卡作为DPDK的虚拟设备进行管理。网卡绑定的过程包括以下几个步骤: 1. 加载IGB UIO驱动程序:使用modprobe命令加载驱动程序,驱动程序所在路径在DPDK目录下的x86_64-native-linuxapp-gcc/kmod目录中。 2. 绑定网卡到UIO驱动程序上:使用DPDK提供的dpdk-devbind工具,输入命令进行网卡与驱动程序的绑定,如:./dpdk-devbind --bind=uio_pci_generic eth0。 3. 初始化DPDK虚拟设备:使用DPDK提供的脚本,输入命令进行虚拟设备的初始化,如:./usertools/dpdk-devbind.py -s。 网卡解绑是将DPDK与物理网卡连接断开的过程。解绑网卡的过程包括以下几个步骤: 1. 停止DPDK进程:使用DPDK提供的脚本,输入命令停止DPDK进程,如:./usertools/dpdk-devbind.py --status。 2. 解绑网卡:使用DPDK提供的dpdk-devbind工具,输入命令解绑网卡,如:./dpdk-devbind --unbind eth0。 3. 卸载IGB UIO驱动程序:使用rmmod命令卸载驱动程序,如:rmmod uio_pci_generic。 绑定解绑网卡的操作可以通过脚本进行自动化,以提高效率和可靠性。同时也需要注意,绑定解绑不当会导致系统网络异常,因此需要进行充分测试和验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值