当前位置: 首页 > news >正文

汽车贸易网站建设方案品牌策划案案例

汽车贸易网站建设方案,品牌策划案案例,人才网站开发文档,浙江建设信息港电工证查询目录 示例 大体流程 配置器 OAuth2AuthorizationServerConfigurer createConfigurers ​​​​​​​configure 过滤器 ​​​​​​​OAuth2TokenEndpointFilter ​​​​​​​自定义配置 配置示例 ​​​​​​​tokenEndpoint ​​​​​​​自定义请求参数转换…

目录

示例

大体流程

配置器

OAuth2AuthorizationServerConfigurer

createConfigurers

​​​​​​​configure

过滤器

​​​​​​​OAuth2TokenEndpointFilter

​​​​​​​自定义配置

配置示例

​​​​​​​tokenEndpoint

​​​​​​​自定义请求参数转换器

CustomDelegatingAuthenticationConverter

​​​​​​​PasswordGrantAuthenticationConverter

​​​​​​​PasswordRefreshTokenAuthenticationConverter

自定义认证提供者

PasswordGrantAuthenticationProvider

​​​​​​​PasswordRefreshTokenAuthenticationProvider

时序图


        本篇文章我们来研究spring-security5框架如何实现OAuth2的密码授权模式,即用户向客户端提供自己的用户名和密码,客户端使用这些信息向“服务提供商”索要授权。

        先从一个简单示例开始,如下图所示:

示例

        首先,新建一个config包用于存放spring-security通用配置;

        然后,新建一个AuthSecurityConfig类,给AuthSecurityConfig类中加上@EnableWebSecurity 注解后,这样便会自动被 Spring发现并注册。

@Configuration

@EnableWebSecurity

public class AuthSecurityConfig{

  @Bean

  @Order(1)

  public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,                                                                   OAuth2AuthorizationService authorizationService,                                                               OAuth2TokenGenerator<?> tokenGenerator) throws Exception {

      OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =

                new OAuth2AuthorizationServerConfigurer<>();

      authorizationServerConfigurer.tokenEndpoint(tokenEndpoint ->

        tokenEndpoint

.accessTokenRequestConverter(new CustomDelegatingAuthenticationConverter())

.authenticationProvider(new PasswordGrantAuthenticationProvider())

          .authenticationProvider(new PasswordRefreshTokenAuthenticationProvider()));

RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

      http.requestMatcher(endpointsMatcher)

          .apply(authorizationServerConfigurer);

      return http.build();

  }

        在这里,首先实例化一个配置器OAuth2AuthorizationServerConfigurer对象;

        然后,自定义accessTokenRequestConverter和authenticationProvider配置信息;

        最后,将该配置器对象应用到HttpSecurity 对象即可。

大体流程

        点击示例里的OAuth2AuthorizationServerConfigurer类,如下所示:

​​​​​​​配置器

OAuth2AuthorizationServerConfigurer

public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBuilder<B>>

extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer<B>, B> {

private final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = createConfigurers();

        在这里,调用createConfigurers()方法,创建各种相关的子配置器对象。

        点击createConfigurers()方法,如下所示:

​​​​​​​createConfigurers

private Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> createConfigurers() {

Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();

configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess));

configurers.put(OAuth2AuthorizationEndpointConfigurer.class, new OAuth2AuthorizationEndpointConfigurer(this::postProcess));

configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));

configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));

configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess));

configurers.put(OidcConfigurer.class, new OidcConfigurer(this::postProcess));

return configurers;

}

        在这里,我们看到程序创建了配置器OAuth2TokenEndpointConfigurer对象。配置器对象创建好了之后,重点要关注该对象的configure()方法。

        点击对象的configure()方法,如下所示:

​​​​​​​configure

public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer {

  ... ...

@Override

<B extends HttpSecurityBuilder<B>> void configure(B builder) {

AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);

ProviderSettings providerSettings = OAuth2ConfigurerUtils.getProviderSettings(builder);

OAuth2TokenEndpointFilter tokenEndpointFilter =

new OAuth2TokenEndpointFilter(

authenticationManager,

providerSettings.getTokenEndpoint());

if (this.accessTokenRequestConverter != null) {

tokenEndpointFilter.setAuthenticationConverter(this.accessTokenRequestConverter);

}

... ...

builder.addFilterAfter(postProcess(tokenEndpointFilter), FilterSecurityInterceptor.class);

}

...

}

       在这里,我们看到程序创建了过滤器OAuth2TokenEndpointFilter对象,并且传入请求参数转换器对象accessTokenRequestConverter和认证管理器对象authenticationManager。

        过滤器对象如下所示:

过滤器

​​​​​​​OAuth2TokenEndpointFilter

public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter {

... ...

@Override

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

if (!this.tokenEndpointMatcher.matches(request)) {

filterChain.doFilter(request, response);

return;

}

try {

... ...

Authentication authorizationGrantAuthentication = this.authenticationConverter.convert(request);

... ...

OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =

(OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication);

this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, accessTokenAuthentication);

} catch (OAuth2AuthenticationException ex) {

... ...

}

}

private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {

... ...

}

}

        在这里,我们看到整个过滤逻辑主要包括请求参数转换操作convert(request)和认证操作authenticate(authorizationGrantAuthentication)两个。

        请求参数转换操作由authenticationConverter对象来处理,该对象在实例化过滤器时传入。

        认证操作由authenticationManager对象处理,该对象在实例化过滤器时传入。

        具体实现逻辑见后续章节。

​​​​​​​自定义配置

配置示例

@Configuration

@EnableWebSecurity

public class AuthSecurityConfig{

  @Bean

  @Order(1)

  public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,                                                                   OAuth2AuthorizationService authorizationService,                                                               OAuth2TokenGenerator<?> tokenGenerator) throws Exception {

      OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =

                new OAuth2AuthorizationServerConfigurer<>();

      authorizationServerConfigurer.tokenEndpoint(tokenEndpoint ->

        tokenEndpoint

.accessTokenRequestConverter(new CustomDelegatingAuthenticationConverter())

.authenticationProvider(new PasswordGrantAuthenticationProvider())

          .authenticationProvider(new PasswordRefreshTokenAuthenticationProvider()));

RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

      http.requestMatcher(endpointsMatcher)

          .apply(authorizationServerConfigurer);

      return http.build();

  }

       在这里,通过调用tokenEndpoint()方法实现对请求参数转换器和认证提供者的自定义。传入的匿名内部类,采用链式调用的方式先自定义请求参数转换器CustomDelegatingAuthenticationConverter,再自定义认证提供者PasswordGrantAuthenticationProvider和PasswordRefreshTokenAuthenticationProvider。

​​​​​​​tokenEndpoint

public OAuth2AuthorizationServerConfigurer<B> tokenEndpoint(Customizer<OAuth2TokenEndpointConfigurer> tokenEndpoint(Customizer) {

tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class));

return this;

}

       在这里,通过调用getConfigurer()方法获取配置器实例OAuth2TokenEndpointConfigurer,然后将该实例传入给匿名内部类的接口方法。

​​​​​​​自定义请求参数转换器

CustomDelegatingAuthenticationConverter

        这是一个转换器的代表类,代表如下两个转换器类:PasswordGrantAuthenticationConverter、PasswordRefreshTokenAuthenticationConverter。

@Slf4j

public class CustomDelegatingAuthenticationConverter implements AuthenticationConverter {

    private final List<AuthenticationConverter> converters;

    public CustomDelegatingAuthenticationConverter() {

        this.converters = Arrays.asList(

            new PasswordGrantAuthenticationConverter(),

            new PasswordRefreshTokenAuthenticationConverter());

    }

    @Nullable

    @Override

    public Authentication convert(HttpServletRequest request) {

        for (AuthenticationConverter converter : this.converters) {

            Authentication authentication = converter.convert(request);

            if (authentication != null) {

                return authentication;

            }

        }

        log.error("没有匹配到合适的Converter");

        throw new BusinessException(ResponseCode.BUSINESS_AUTH_ERROR.getCode());

    }

}

       在这里,按顺序调用每个转换器实例的convert()方法,只要调用结果返回不为null则表示调用成功,成功则结束convert操作;如果遍历所有的转换器都没有调用成功,则抛出异常。

​​​​​​​PasswordGrantAuthenticationConverter

public class PasswordGrantAuthenticationConverter implements AuthenticationConverter {

    public PasswordGrantAuthenticationConverter() { }

    @Override

    public Authentication convert(HttpServletRequest request) {

        // 判断登录授权模式是否是支持的类型

        String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);

        String username = request.getParameter(OAuth2ParameterNames.USERNAME);

        if (!”password”.equals(grantType) || StringUtils.isEmpty(username)) {

            return null;

        }

        ... ...

        Authentication clientPrincipal=securityContextHolder.getContext().getAuthentication();

        // 获取用户名与密码,并校验密码的合法性

        String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);

        // 获取用户信息

        CustomUserDetails userDetails = customUserDetailsService.loadUser(username, password);

        // 返回自定义的PasswordGrantAuthenticationToken对象

        return new PasswordGrantAuthenticationToken(clientPrincipal, additionalParameters,

                , userDetails);

    }

}

        在这里,将用户输入的username 和password 转换为Authentication对象。

​​​​​​​PasswordRefreshTokenAuthenticationConverter

public class PasswordRefreshTokenAuthenticationConverter implements AuthenticationConverter {

@Override

public Authentication convert(HttpServletRequest request) {

String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);

if (!AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(grantType)) {

return null;

}

    Authentication clientPrincipal=SecurityContextHolder.getContext().getAuthentication();

String refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN);

String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);

Set<String> requestedScopes = Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));

    ... ...

return new OAuth2RefreshTokenAuthenticationToken(

refreshToken, clientPrincipal, requestedScopes, additionalParameters);

}

}

        在这里,将用户输入的refreshToken 转换为Authentication对象。

自定义认证提供者

PasswordGrantAuthenticationProvider

public class PasswordGrantAuthenticationProvider implements AuthenticationProvider {

    public PasswordGrantAuthenticationProvider() {}

    @Override

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        //获取自定义token信息

        PasswordGrantAuthenticationToken passwordGrantAuthenticationToken =

                (PasswordGrantAuthenticationToken) authentication;

        ... ...

        //授权类型

        AuthorizationGrantType authorizationGrantType = passwordGrantAuthenticationToken.getGrantType();

        //密码

        String password = (String)additionalParameters.get(OAuth2ParameterNames.PASSWORD);

        //用户信息

        CustomUserDetails userDetails = passwordGrantAuthenticationToken.getUserDetails();

        // Ensure the client is authenticated

        OAuth2ClientAuthenticationToken clientPrincipal =

  AuthUtils.getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken);

        ... ...

        // 由于在上面已验证过用户名、密码,现在构建一个已认证的对象

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =

                new UsernamePasswordAuthenticationToken(userDetails, password);

        DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()

                .registeredClient(registeredClient)

                .principal(usernamePasswordAuthenticationToken)

                .providerContext(ProviderContextHolder.getProviderContext())

                .tokenType(OAuth2TokenType.ACCESS_TOKEN)

                .authorizationGrantType(authorizationGrantType)

                .authorizedScopes(registeredClient.getScopes())

                .authorizationGrant(passwordGrantAuthenticationToken);

        // Initialize the OAuth2Authorization

        OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)

                .principalName(clientPrincipal.getName())

                .attribute(Principal.class.getName(), usernamePasswordAuthenticationToken)

                .attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, registeredClient.getScopes()).authorizationGrantType(authorizationGrantType);

        // ----- Access token -----

        OAuth2AccessToken accessToken = getAccessToken(tokenContextBuilder, authorizationBuilder);

        // ----- Refresh token -----

        OAuth2RefreshToken refreshToken =

            getRefreshToken(registeredClient, clientPrincipal, tokenContextBuilder, authorizationBuilder);

        ... ...

        //存储token信息

        authorizationService.save(authorization);

        return new PasswordTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);

    }

    @Override

    public boolean supports(Class<?> authentication) {

        return PasswordGrantAuthenticationToken.class.isAssignableFrom(authentication);

    }

}

        在这里,验证用户输入的username 和password的合法性。

        如果合法,则生成accessToken 和refreshToken ,然后返回给用户。

​​​​​​​PasswordRefreshTokenAuthenticationProvider

public class PasswordRefreshTokenAuthenticationProvider implements AuthenticationProvider {

public PasswordRefreshTokenAuthenticationProvider() {}

@Override

public Authentication authenticate(Authentication authentication) {

OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication =

(OAuth2RefreshTokenAuthenticationToken) authentication;

OAuth2ClientAuthenticationToken clientPrincipal =

AuthUtils.getAuthenticatedClientElseThrowInvalidClient(refreshTokenAuthentication);

RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

OAuth2Authorization authorization = this.authorizationService.findByToken(

refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN);

if (authorization == null || registeredClient == null ) {

log.error("Authentication或registeredClient的信息为空!");

throw new BusinessException(ResponseCode.BUSINESS_AUTH_ERROR.getCode());

}

if (!registeredClient.getId().equals(authorization.getRegisteredClientId()) ||

!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {

log.error("registeredClient信息不符合要求!");

throw new BusinessException(ResponseCode.BUSINESS_AUTH_ERROR.getCode());

}

OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken();

... ...

//获取token构造时存储的用户密码认证对象

UsernamePasswordAuthenticationToken principal = authorization.getAttribute(Principal.class.getName());

DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()

.registeredClient(registeredClient)

.principal(principal)

.providerContext(ProviderContextHolder.getProviderContext())

.authorization(authorization)

.authorizedScopes(scopes)

.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)

.authorizationGrant(refreshTokenAuthentication);

OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization);

// ----- Access token -----

OAuth2AccessToken accessToken = getOAuth2AccessToken(tokenContextBuilder, authorizationBuilder);

// ----- Refresh token -----

OAuth2RefreshToken currentRefreshToken = getOAuth2RefreshToken(refreshToken, registeredClient,tokenContextBuilder, authorizationBuilder);

        ... ...

    return new OAuth2AccessTokenAuthenticationToken(

registeredClient, clientPrincipal, accessToken, currentRefreshToken, additionalParameters);

}

@Override

public boolean supports(Class<?> authentication) {

  return OAuth2RefreshTokenAuthenticationToken.class.isAssignableFrom(authentication);

}

}

        在这里,验证用户输入的refreshToken 的合法性。

        如果合法,则生成新的accessToken 和refreshToken返回给用户。

时序图

  1. 类对象主要包括:配置器对象OAuth2TokenEndpointConfigurer、过滤器对象OAuth2TokenEndpointFilter、请求参数转换器对象CustomDelegatingAuthenticationConverter、认证管理器对象ProviderManager;
  2. 配置器对象:使用模板方法设计模式,提供了init、beforeConfigure、configure等几个主要的过程来对过滤器进行配置;
  3. 过滤器对象:过滤器的过滤逻辑不仅简单也很清晰,即只包含了convert和authenticate两个过程;
  4. 请求参数转换器对象:框架提供了由开发人员自定义请求参数转换器的功能,请求参数转换器的主要功能是把HTTP请求参数封装为框架需要的请求参数类;
  5. 认证管理器对象:认证管理器管理着多个认证提供者,框架提供了由开发人员自定义认证提供者的功能。
http://www.yayakq.cn/news/81894/

相关文章:

  • 可以免费发布招聘网站男友给女朋友做网站
  • 网站建设费用首选网络河北建筑工程学院招生网官网
  • 南京网站建设报价企业信息网站
  • 广东 网站建设 公司排名自己有网站做点什么
  • 杭州做网站公司17网站一起做网店揭阳
  • dedecms 建两个网站的问题中核集团八大子公司
  • 台山网站设计网站 设计 分辨率
  • 网站接入协议及接入商资质地方门户网站如何盈利
  • 如何做弹幕视频网站自己做网站制作
  • 网站建设裕鸿国际佛山按天网站优化服务
  • 如何利用源码做网站东莞关键词优化代理
  • 天津做网站一般多少钱神网站建设
  • 体检中心 网站建设方案企业网站建设前网站目的需明确
  • 网站建设一般用英文怎么说自己做的网站怎么放到网上去
  • 连云港网站 建设wordpress后台颜色
  • 龙岩做网站龙岩网页设计线上app怎么做
  • 建设国家标准官方网站邯郸网站建设xy0310
  • 昆明网站建设开发外包it培训机构培训排名
  • 陕西省建设部网站网站设计优秀作品
  • 代刷网站建设汝州建站公司
  • 河东建设局网站google搜索引擎入口下载
  • 抚顺外贸网站建设建站软件安卓
  • seo网站怎么建设大型大型网站制作
  • 多媒体教学网站的建设的论文蜜雪冰城用到了哪些网络营销方式
  • 企业网站百度收录做直播教程的网站有哪些
  • 淄博网站建设设计新手做淘宝哪个网站比较好
  • 成都网站制作工具软件界面设计教程
  • 看汽车图片的网站可以做壁纸网站开发大致需要哪些步骤
  • 高端医院网站建设打开qq登录网页
  • 天津 网站设计制作公司苏州市郭巷建设局网站