如何在分布式环境中搭建单点登录系统| 第二篇:基于Oauth2.0开发SSO核心代码

什么是SSO

单点登录系统主要解决统一认证授权的问题,在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
想要完成单点登录的效果,必须先统一管理用户信息,其他应用系统必须配合完成改造和对接。

什么场景下,需要用到SSO?

较大型的企业,往往存在多套应用系统。各个应用系统都是在企业发展的某个阶段,因业务发展的需求,开发研制而成。每套系统都会有一套自己的用户体系,需要终端用户注册、登录后才能使用。
随着企业的发展,用到的系统随之增多,用户在操作不同的系统时,需要多次登录,而且每个系统的账号都不一样,这对于用户来说很不方便。
于是,设计一套统一的登录认证系统,避免不必要的反复登录。减轻用户操作负担,提高效率,在企业的发展进程中,显得越来越重要。

什么是OAuth 2.0

OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容。
OAuth 2.0是OAuth协议的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。

为什么需要OAuth 2.0?

上文提到,为了实现SSO,各个应用系统需要配合对接单点登录系统。OAuth2.0提供了一套简单,通用,可扩展的开放认证授权协议。既可以实现企业内部系统的对接,也可以实现企业与第三方外部系统的认证互通。
鉴于OAuth2.0的开放特性,只要遵守OAuth2.0协议,可以大幅降低系统间对接开发的沟通调试成本。

什么是Spring Security OAuth

Spring Security对OAuth的实现,借助SpringSecurity框架在认证授权方面既有的优势,可以让开发者简易地使用OAuth协议。

image.png

Spring Security OAuth存在的问题

  • 鉴于OAuth协议本身就有较多的概念(4种角色,4种授权模式),使用Spring Security OAuth就需要定义和管理OAuth相关的Bean。
  • 另一个比较大的问题,在于Spring Security OAuth默认基于Session实现,这在微服务场景下并不适用。这也是本系列文章要解决的核心问题。

代码实现

Springsecurity基础配置

  • 声明认证管理器AuthenticationManager,认证阶段的用户身份鉴别使用自定义的UserDetailsService
  • 采用BCryptPasswordEncoder对登录密码进行加密及编码,BCryptPasswordEncoder基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
  • 自定义认证逻辑过滤器,满足登录请求参数个性化,多样化需求
  • 自定义token解析过滤器,解决微服务无状态场景下,Spring Security OAuth无法在Session中获取用户认证信息的问题
package com.codeiy.auth.oauth.config;

import com.codeiy.auth.oauth.filter.AuthenticationProcessingFilter;
import com.codeiy.auth.oauth.filter.TokenAuthenticationFilter;
import com.codeiy.auth.oauth.handler.OAuthFailureHandler;
import com.codeiy.auth.oauth.handler.OAuthSuccessHandler;
import com.codeiy.auth.oauth.handler.SsoLogoutSuccessHandler;
import com.codeiy.auth.oauth.service.UserDetailsServiceImpl;
import com.codeiy.core.constant.AuthConstants;
import com.codeiy.user.client.UserClient;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
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.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.Filter;

/**
 * SpringSecurity基础配置
 * 声明认证管理器{@link AuthenticationManager},认证阶段的用户身份鉴别使用自定义的{@link UserDetailsService}
 * 采用{@link BCryptPasswordEncoder}对登录密码进行加密及编码,{@link BCryptPasswordEncoder}基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
 *
 * @author free@codeiy.com
 */
@Primary
@Order(90)
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    private final UserClient userClient;
    /**
     * 基于随机盐+密钥对密码进行加密,并通过SHA-256算法进行编码
     */
    PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    /**
     * 自定义身份认证服务
     */
    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        UserDetailsServiceImpl userDetailsService = new UserDetailsServiceImpl();
        userDetailsService.setUserClient(userClient);
        userDetailsService.setPasswordEncoder(passwordEncoder);
        return userDetailsService;
    }

    /**
     * 1. 禁用csrf
     * 2. 请求会话采用无状态方式
     * 3. 自定义登录登出路径
     * 4. 自定义登录请求参数{@link UsernamePasswordAuthenticationFilter}
     *
     * @param http http请求安全相关配置
     */
    @Override
    @SneakyThrows
    protected void configure(HttpSecurity http) {
        Filter loginFilter = loginFilter();
        Filter tokenAuthenticationFilter = tokenAuthenticationFilter();
        http.formLogin()
                .loginProcessingUrl(AuthConstants.LOGIN_PROCESSING_URL).permitAll()
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().logout().logoutSuccessHandler(new SsoLogoutSuccessHandler())
                .and().authorizeRequests()
                .antMatchers(AuthConstants.LOGOUT_URL).permitAll()
                .anyRequest().authenticated()
                // 登录请求参数除了用户名,密码之外,还有验证码等其他参数,通过过滤器自定义认证逻辑
                .and().addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class)
                // 解决微服务架构无状态请求场景下,如何识别当前请求所属用户
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只要你愿意我愿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值