spring oauth2 实现社交登录返回token

介绍

上一章讲到了自定义登录返回token的方式登录。但如果用户要社交登录,同样也得返回token给用户。社交登录有两种模式,一种是app传providerId和openId,一种是app传了code过来。这里先讲第一种,

实现功能

社交登录,app传来providerId和openId登录返回token。

步骤

实现前提:该providerId和openId必须在数据库中存在。

实现的过程其实和手机号验证码登录一样。

编写OpenIdAuthenticationToken
/**
 * @author lvhaibao
 * @description
 * @date 2019/1/8 0008 14:40
 */
public class OpenIdAuthenticationToken  extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 420L;

    /**
     * openid
     */
    private final Object principal;

    /**
     * 服务提供上ID
     */
    private String providerId;


    OpenIdAuthenticationToken(String openId, String providerId) {
        super(null);
        this.principal =openId;
        this.providerId = providerId;
        setAuthenticated(false);
    }

    OpenIdAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal =principal;
        super.setAuthenticated(true);
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public String getProviderId() {
        return providerId;
    }

    public void setProviderId(String providerId) {
        this.providerId = providerId;
    }


}
编写OpenIdAuthenticationProvider
/**
 * @author lvhaibao
 * @description
 * @date 2019/1/8 0008 14:46
 */
public class OpenIdAuthenticationProvider implements AuthenticationProvider {

    private SocialUserDetailsService myUserDetailsService;

    private UsersConnectionRepository usersConnectionRepository;

    @Override
    public boolean supports(Class<?> authentication) {
        //支持OpenIdAuthenticationToken来验证
        return OpenIdAuthenticationToken.class.isAssignableFrom(authentication);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        //这个authentication就是OpenIdAuthenticationToken
        OpenIdAuthenticationToken authenticationToken = (OpenIdAuthenticationToken) authentication;

        //校验手机号
        Set<String> providerUserIds = new HashSet<>();
        providerUserIds.add((String) authenticationToken.getPrincipal());
        Set<String> userIds = usersConnectionRepository.findUserIdsConnectedTo(authenticationToken.getProviderId(),providerUserIds);

        if(CollectionUtils.isEmpty(userIds) || userIds.size() != 1){
            throw new InternalAuthenticationServiceException("无法获取用户信息");
        }

        String userId = userIds.iterator().next();

        UserDetails user = myUserDetailsService.loadUserByUserId(userId);

        if(user == null ){
            throw new InternalAuthenticationServiceException("无法获取用户信息");
        }


        //这时候已经认证成功了
        OpenIdAuthenticationToken authenticationResult = new OpenIdAuthenticationToken(user, user.getAuthorities());
        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    public SocialUserDetailsService getMyUserDetailsService() {
        return myUserDetailsService;
    }

    public void setMyUserDetailsService(SocialUserDetailsService myUserDetailsService) {
        this.myUserDetailsService = myUserDetailsService;
    }

    public UsersConnectionRepository getUsersConnectionRepository() {
        return usersConnectionRepository;
    }

    public void setUsersConnectionRepository(UsersConnectionRepository usersConnectionRepository) {
        this.usersConnectionRepository = usersConnectionRepository;
    }
}

编写OpenIdAuthenticationFilter
/**
 * @author lvhaibao
 * @description
 * @date 2019/1/8 0008 14:52
 */
public class OpenIdAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private String openIdParameter = "openId";
    private String providerIdParameter = "providerId";

    /**
     * 是否只处理post请求
     */
    private boolean postOnly = true;


    public OpenIdAuthenticationFilter(){
        //要拦截的请求
        super(new AntPathRequestMatcher("/authentication/openid", "POST"));
    }


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String openId = this.obtainOpenId(request);
            String providerId = this.obtainProvider(request);
            if (openId == null) {
                openId = "";
            }

            if(providerId == null){
                providerId = "";
            }


            openId = openId.trim();
            providerId = providerId.trim();


            //把手机号传进SmsCodeAuthenticationToken
            OpenIdAuthenticationToken authRequest = new OpenIdAuthenticationToken(openId, providerId);
            this.setDetails(request, authRequest);
            //调用AuthenticationManager
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }









    /**
     * 获取openId
     *
     * @param request request
     * @return String
     */
    private String obtainOpenId(HttpServletRequest request) {
        return request.getParameter(this.openIdParameter);
    }

    private String obtainProvider(HttpServletRequest request){
        return request.getParameter(this.providerIdParameter);
    }


    private void setDetails(HttpServletRequest request, OpenIdAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setOpenIdParameter(String openIdParameter) {
        Assert.hasText(openIdParameter, "Username parameter must not be empty or null");
        this.openIdParameter = openIdParameter;
    }


    public final String getOpenIdParameter() {
        return this.openIdParameter;
    }


    public String getProviderIdParameter() {
        return providerIdParameter;
    }

    public void setProviderIdParameter(String providerIdParameter) {
        this.providerIdParameter = providerIdParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}
编写配置
/**
 * @author lvhaibao
 * @description
 * @date 2019/1/8 0008 14:54
 */
@Component
public class OpenIdAuthenticationConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Autowired
    private AuthenticationSuccessHandler myAuthenticationSuccessHandler;


    @Autowired
    private SocialUserDetailsService userDetailsService;

    @Autowired
    private UsersConnectionRepository connectionRepository;


    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private ConnectionFactoryLocator connectionFactoryLocator;

    @Bean
    public AppSignUpUtils appSignUpUtils(){
        return new AppSignUpUtils(redisTemplate, connectionRepository, connectionFactoryLocator);
    }



    @Override
    public void configure(HttpSecurity http) throws Exception {
        OpenIdAuthenticationFilter openIdAuthenticationFilter = new OpenIdAuthenticationFilter();

        //设置AuthenticationManager
        openIdAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        //设置成功失败处理器
        openIdAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);


        //设置provider
        OpenIdAuthenticationProvider openIdAuthenticationProvider = new OpenIdAuthenticationProvider();
        openIdAuthenticationProvider.setMyUserDetailsService(userDetailsService);
        openIdAuthenticationProvider.setUsersConnectionRepository(connectionRepository);

        http.authenticationProvider(openIdAuthenticationProvider)
                .addFilterAfter(openIdAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }
}
在websecurity添加配置
/**
 * @author lvhaibao
 * @description 浏览器配置
 * @date 2018/12/25 0025 10:53
 */
@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SecurityProperties securityProperties;
    @Autowired
    private VcodeManager vcodeManager;
    @Autowired
    private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig;
    @Autowired
    private OpenIdAuthenticationConfig openIdAuthenticationConfig;
    @Autowired
    private SpringSocialConfigurer mySocialSecurityConfig;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private AuthenticationSuccessHandler myAuthenticationSuccessHandler;


//    @Override
//    @Bean
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }


    /**
     * 生成记得我的token
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        //使用jdbc来存储
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        //设置数据源
        tokenRepository.setDataSource(dataSource);
        //当为true的时候就会自动创建表
        //tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

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

        SmsCodeFilter smsCodeFilter = new SmsCodeFilter(vcodeManager);
        smsCodeFilter.setSecurityProperties(securityProperties);
        smsCodeFilter.afterPropertiesSet();

        http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
                //表单登录,loginPage为登录请求的url,loginProcessingUrl为表单登录处理的URL
                .formLogin().loginPage(FromLoginConstant.LOGIN_PAGE).loginProcessingUrl(FromLoginConstant.LOGIN_PROCESSING_URL)
                //登录成功之后的处理
                .successHandler(myAuthenticationSuccessHandler)
                //允许访问
                .and().authorizeRequests().antMatchers(
                FromLoginConstant.LOGIN_PROCESSING_URL,
                FromLoginConstant.LOGIN_PAGE,
                securityProperties.getOauthLogin().getOauthLogin(),
                securityProperties.getOauthLogin().getOauthGrant(),
                "/myLogout",
                "/code/sms")
//                "/oauth/**")
                .permitAll().anyRequest().authenticated()
                //禁用跨站伪造
                .and().csrf().disable()
                //短信验证码配置
                .apply(smsCodeAuthenticationSecurityConfig)
                //社交登录
                .and().apply(mySocialSecurityConfig)
                //openID登录
                .and().apply(openIdAuthenticationConfig);

    }


}
测试

使用http工具post请求:http://www.pinzhi365.com/authentication/openid。 如图所示:

在这里插入图片描述

即可返回token。

项目源码

https://gitee.com/lvhaibao/spring-lhbauth/tree/f15183a593f64d6a9494f1800f9b8ce4498111ae/

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值