自定义JWT认证转换器不生效

来源:8-6 前端为例详解授权码流程

叫我滨哥哥

2023-09-15

老师,我这边跟您一样的代码,但是customJwtAuthenticationTokenConverter这个方法一直不会进,导致现在token里没有Role_的角色,是哪里缺少配置了

@Slf4j
@EnableWebSecurity(debug = true)
@RequiredArgsConstructor
@Import(SecurityProblemSupport.class)
@Order(99)
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
    private final SecurityProblemSupport securityProblemSupport;
    private final RoleHierarchyService roleHierarchyService;
    private final UserDetailsPasswordService userDetailsPasswordService;
    private final UserDetailsService userDetailsService;
    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    private String jwkSetUri;

    /**
     * 主要用来配置资源的安全性
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .requestMatchers(req -> req.mvcMatchers("/api/**", "/admin/**"))//匹配这些指定请求,来做下面这些配置
                .csrf(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .httpBasic(AbstractHttpConfigurer::disable)
                .logout(AbstractHttpConfigurer::disable)
                .sessionManagement(sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(exceptionHandling -> exceptionHandling
                        .authenticationEntryPoint(securityProblemSupport)
                        .accessDeniedHandler(securityProblemSupport))
                .authorizeRequests(req -> req
                        .mvcMatchers("/authorize/**").permitAll()
                        .mvcMatchers("/admin/**").hasAnyAuthority("SCOPE_user.admin", "SCOPE_client.admin")
                        .mvcMatchers("/api/users/by-email/**").hasAuthority(Constants.AUTHORITY_USER_READ)
                        .mvcMatchers("/api/users/{username}/**").access("hasRole('" + Constants.AUTHORITY_ADMIN + "') or @userValidationService.checkUsername(authentication, #username)")
                        .mvcMatchers("/api/**").authenticated()//authenticated() 这个是认证,只要认证过了,就可以访问该资源
                        .anyRequest().denyAll())//剩下的请求全部拒绝
//                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)//这个的jwttoken是带scope的,跟没有role那些
                //自定义的JWT认证转换器
                .oauth2ResourceServer(resourceServer -> resourceServer.jwt().jwtAuthenticationConverter(customJwtAuthenticationTokenConverter()));

        ;
    }

    /**
     * 自定义JWT认证转换器
     *
     * @return
     */
    private Converter<Jwt, AbstractAuthenticationToken> customJwtAuthenticationTokenConverter() {
        return jwt -> {
            List<String> userAuthorities = jwt.getClaimAsStringList("authorities");
            List<String> scopes = jwt.getClaimAsStringList("scope");
            List<GrantedAuthority> combinedAuthorities = Stream.concat(
                            userAuthorities.stream(),
                            scopes.stream().map(scope -> "SCOPE_" + scope))
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());
            String username = jwt.getClaimAsString("user_name");
            return new UsernamePasswordAuthenticationToken(username, null, combinedAuthorities);
        };
    }

    @Override
    public void configure(WebSecurity web) {
        web
                .ignoring()
                .antMatchers("/resources/**", "/static/**", "/public/**", "/h2-console/**", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**")
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .userDetailsService(userDetailsService) // 配置 AuthenticationManager 使用 userService
                .passwordEncoder(passwordEncoder()) // 配置 AuthenticationManager 使用 userService
                .userDetailsPasswordManager(userDetailsPasswordService); // 配置密码自动升级服务
    }

    /**
     * 如果想让 Spring Security OAuth2 支持 password 这种 grant_type ,那么此处必须要暴露出 AuthenticationManager
     *
     * @return AuthenticationManager
     * @throws Exception 异常
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        String idForEncode = "bcrypt";//定义默认的密码编码器
        Map<String, PasswordEncoder> encoders = new HashMap<>(2);
        encoders.put(idForEncode, new BCryptPasswordEncoder());
        encoders.put("noop", NoOpPasswordEncoder.getInstance());
//        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
//        encoders.put("scrypt", new SCryptPasswordEncoder());
//        encoders.put("sha256", new StandardPasswordEncoder());
//        encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
        return new DelegatingPasswordEncoder(idForEncode, encoders);
    }

    //    @ConditionalOnProperty(prefix = "mooc.security", name = "role-hierarchy-enabled", havingValue = "true")
    @Bean
    public RoleHierarchyImpl roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy(roleHierarchyService.getRoleHierarchyExpr());
        return roleHierarchy;
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
    }

}
@RequiredArgsConstructor
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    private final KeyPair keyPair;
    private final DataSource dataSource;
    private final PasswordEncoder passwordEncoder;
    private final AuthenticationManager authenticationManager;
    private final UserDetailsService userDetailsService;

    /**
     * 配置授权服务器的 token 接入点
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //获取token不需要权限
                .tokenKeyAccess("permitAll()")
                //token的校验必须是认证过的
                .checkTokenAccess("isAuthenticated()")
                //允许表单提交
                .allowFormAuthenticationForClients();
    }

    /**
     * 配置 Jdbc 版本的 JdbcClientDetailsService
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients
                .jdbc(dataSource)
                .passwordEncoder(passwordEncoder);
    }

    /**
     * 配置授权访问的接入点
     *
     * @param endpoints
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        TokenEnhancerChain chain = new TokenEnhancerChain();
        chain.setTokenEnhancers(Collections.singletonList(accessTokenConverter()));
        endpoints
//            .accessTokenConverter(accessTokenConverter())//二选一
                .tokenEnhancer(chain)//二选一
                .tokenStore(tokenStore()) // 从 token 中读取特定字段构成 Authentication
                .authenticationManager(authenticationManager) // 配置认证 manager
                .userDetailsService(userDetailsService); // 配置用户
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(this.keyPair);
        return converter;
    }

    /**
     * 配置密码编码器
     *
     * @return
     */
    @Bean
    public JdbcClientDetailsService jdbcClientDetailsService() {
        val service = new JdbcClientDetailsService(dataSource);
        service.setPasswordEncoder(passwordEncoder);
        return service;
    }
}
写回答

2回答

接灰的电子产品

2023-09-16

使用git 代码试试,应该好用的

0
0

接灰的电子产品

2023-09-16

这里二选一,注释掉了,

endpoints
//            .accessTokenConverter(accessTokenConverter())//二选一
                .tokenEnhancer(chain)//二选一
0
1
叫我滨哥哥
我试过了,即使没注释没进不去。。。。到底是啥原因呢?
2023-09-16
共1条回复

Spring Security+OAuth2 精讲,打造企业级认证与授权

一站式掌握主流安全框架与行业解决方案,从容应对各种安全难题。

1043 学习 · 316 问题

查看课程