美文网首页技术架构
05 Spring Social开发第三方登陆(未完)

05 Spring Social开发第三方登陆(未完)

作者: Koko_滱滱 | 来源:发表于2018-04-22 22:30 被阅读123次

OAuth协议

OAuth协议解决什么问题

  • 需求场景
    有一个服务,用于为微信拍摄的图片进行美化,这会涉及到一个问题,就是服务怎么去读取微信的数据,这要得到微信的授权。传统上,是把微信的用户名密码给服务,但这会导致以下一个问题
    • 服务可以访问微信的所有数据,无法指定访问范围
    • 只有修改密码才能收回权限
    • 增加了密码泄漏的风险

OAuth解决了上述问题,不直接给用户名密码,而是给token,token 中指定了服务范围和有效时间。


image.png

OAuth协议的角色

  • Privoder:服务提供商
    提供token
    • Authorization Server:认证服务器
      认证用户的身份,并产生token
    • 资源服务器
      保存用户资源
      验证token
  • Resource Owner:资源所有者
  • Client:第三方应用


    image.png

OAuth协议的运行流程

image.png

OAuth授权模式

  • 授权码模式
    四种授权模式中,功能最完整,最严密的授权模式
    是目前主流

    image.png
    • 特点
      用户同意授权的动作是在认证服务器完成的
      认证服务器不直接返回token,而是先返回授权码,由授权码去申请令牌
  • 密码模式

  • 其他

    • 简化模式
    • 客户端模式

小结

OAuth解决了在不给认证信息的情况下,将app权限给第三方应用的场景。

Spring Social基本原理

在使用OAuth的情况下,获取到了微信、QQ等的授权,拿到了用户的基本信息,用这个基本信息作为APP的注册信息进行注册,达到使用QQ、微信登陆APP的效果。

image.png

Spring Social封装了上述1~7个步骤的流程。

image.png

SocialAuthenticationFilter,实现了1~7步

使用Spring Social开发涉及到的若干组件


image.png

QQ登陆

使用第三方账户登陆系统,关键就是走一遍OAuth协议的流程,从服务提供商中获取到用户的基本信息,然后注册到自己的系统中。
//TODO

注册

//TODO

微信登陆

//TODO

绑定与解绑

//TODO

Session管理

用户名密码登陆、手机号短信登陆、QQ登陆、微信登陆,登陆成功后,用户信息是存储在服务器的session中的。
下面就来讲解一下session的基本管理。

session超时处理

用户在一段时间内没有操作,需要将用户从session中剔除掉,而此时用户再发起请求,又该如何处理。
这就是session超时相关的需要解决的问题。

在springboot中设置session超时时间非常简单,加个配置就行

server:
  session:
#  session超时时间,单位秒,默认30分钟
    timeout: 30

注意:如果我们设置的时间少于1分钟,那么超时时间就是一分钟。

下面我们来解决另一个问题,在session超时后,如果用户继续访问,我们得提示用户session超时,而不是其他的引导页跳转等不合适的信息。否则用户会想,我刚才明明已经认证过了,怎么又让我认证一遍。

我们添加以下配置

                .sessionManagement()
                .invalidSessionUrl("/session/invalid")
                .and()

当session超时后,跳转到指定页面
完整的配置类

package com.imooc.security.browser;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import com.imooc.security.core.authentication.AbstractChannelSecurityConfig;
import com.imooc.security.core.authentication.mobile.SmsCodeAuthenticationSecurityConfig;
import com.imooc.security.core.properties.SecurityConstants;
import com.imooc.security.core.properties.SecurityProperties;
import com.imooc.security.core.validate.code.ValidateCodeSecurityConfig;

/**
 * @author zhailiang
 *
 */
@Configuration
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {

    @Autowired
    private SecurityProperties securityProperties;

    @Qualifier("dataSource")
    @Autowired
    private DataSource dataSource;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;

    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        applyPasswordAuthenticationConfig(http);

        http.apply(validateCodeSecurityConfig)
                .and()

                .apply(smsCodeAuthenticationSecurityConfig)
                .and()

                .rememberMe()
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
                .userDetailsService(userDetailsService)
                .and()

                .sessionManagement()
                .invalidSessionUrl("/session/invalid")
                .and()

                .authorizeRequests()
                .antMatchers(
                        SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
                        securityProperties.getBrowser().getLoginPage(),
                        SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()

                .csrf().disable();

    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
//      tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

}

控制器中处理失效请求

    @GetMapping("/session/invalid")
    @ResponseStatus(code = HttpStatus.UNAUTHORIZED)
    public SimpleResponse sessionInvalid() {
        String message = "session失效";
        return new SimpleResponse(message);
    }

注意不要忘记把这个请求地址放到不需要权限控制的url列表中

image.png
这就是session超时失效后,客户端收到的消息

session并发控制

用户在A机器上登陆后,在未退出的情况下,又使用相同账号在B机器上进行了登陆。
我们需要用B登陆覆盖掉A登陆。

  • 配置
                .sessionManagement()
                .invalidSessionUrl("/session/invalid")
                .maximumSessions(1)
                .expiredSessionStrategy(new ImoocExpiredSessionStrategy(SecurityConstants.DEFAULT_SESSION_INVALID_URL))
                .and()
                .and()

我们配置.maximumSessions(1)表示同一个用户只允许有一个session,
然后配置expiredSessionStrategy处理当前session被剔除后的处理。

如果我们不是希望新的登陆去覆盖旧的登陆,而是不允许在旧的登陆未退出的情况下,让新的登陆成功,那么就要加一个配置

.maxSessionsPreventsLogin(true)//session数达到最大值后不允许新的session登陆

集群session管理

默认在单机的情况下,我们的session信息是存储在tomcat等中间件中的,此时如果不做任何处理,那么在集群环境下是会有问题的。
比如用户的登陆认证信息通过负载均衡给了A机器,此时明明已经认证成功了,但是下一个请求发送到了B机器上,B机器上的tomcat没有该用户的session信息,这就导致又得认证一遍,而我们还无法保证下次认证的时候真的是认证在B机器上。

上述问题是在做集群的时候需要考虑的。

解决思路:不把session信息存储在服务器上,而是放在一个独立的存储中。


image.png

spring-session项目就是用来完成上述的行为的,使用起来非常简单。
spring支持的外部存储包括

public enum StoreType {

    /**
     * Redis backed sessions.
     */
    REDIS,

    /**
     * Mongo backed sessions.
     */
    MONGO,

    /**
     * JDBC backed sessions.
     */
    JDBC,

    /**
     * Hazelcast backed sessions.
     */
    HAZELCAST,

    /**
     * Simple in-memory map of sessions.
     */
    HASH_MAP,

    /**
     * No session data-store.
     */
    NONE;

}

下面我们以使用Redis为例

spring:
  session:
#  session集群管理,none表示关闭
    store-type: REDIS

然后再配置下REDIS的连接地址,如果数localhost的6379端口,可以不配置。

注意,现在我们的session已经由redis进行管理了,被redis管理的java类需要实现序列化接口。

使用Redis代替服务器管理session后,对系统没有任何影响,原先的session操作都能使用。

退出登陆

  • 如何退出登陆

  • Spring Security默认退出逻辑

    • 使当前session失效
    • 清除与当前用户相关的remember-me记录
    • 清空当前SecurityContent
    • 重定向到登陆页
  • 相关配置

                .logout()
                .logoutUrl("/logout")//执行退出逻辑的url
                .logoutSuccessUrl("/imooc-logout.html")//退出成功后跳转url
                .and()

也可以使用.logoutSuccessHandler()进行配置,其可以在退出成功后做更复杂的操作,如记录退出日志等。

注意,.logoutSuccessHandler()logoutSuccessUrl()只能二选一进行配置。

添加好友,备注:来自简书,一起 好好学习,天天向上


微信号

相关文章

网友评论

  • b6c7a3ded5ff:@Override
    protected void configure(HttpSecurity http) throws Exception {
    // @formatter:off
    http.cors().and().csrf().disable()
    .anonymous().disable()
    .authorizeRequests()
    .antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
    .and().logout()
    .and().sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .maximumSessions(1)
    .maxSessionsPreventsLogin(true);
    // @formatter:on
    //以下这句就可以控制单个用户只能创建一个session,也就只能在服务器登录一次
    // http.sessionManagement().maximumSessions(1).and().;
    }

    用的oauth2,我配置的不允许账号同时在线没有生效,不知道什么问题,我希望每次用户登陆都能获取新的的token,不知道怎么设置,楼主遇到过没有

本文标题:05 Spring Social开发第三方登陆(未完)

本文链接:https://www.haomeiwen.com/subject/wmyhlftx.html