OAuth2客户端明文和用户密码为密文

一、问题描述:

使用Spring-security-0Auth2:2.5.1 版本,进行用户认证时,一直出现问题:

{
    "error": "invalid_client",
    "error_description": "Bad client credentials"
}

出现问题的条件:

用户的密码使用明文验证时,不会出现此问题。而把密码验证设置为: bcrypt后,再次获取token时,就会报此问题。

postMan的请求和结果如下:

二、代码:

认证服务安全配置类:主要是验证用户名和密码

package com.lagou.edu.oauth.config;


import com.lagou.edu.oauth.service.JdbcUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.ArrayList;

/**
 * FileName: SecurityConfiger
 * Author:   
 * Date:     2021/10/29 17:02
 * Description: 该配置类为认证服务器安全配置类,主要处理用户名和密码的校验等事宜
 */

@Configuration
public class SecurityConfiger extends WebSecurityConfigurerAdapter {
    @Autowired
    private JdbcUserDetailsService jdbcUserDetailsService;

    /**
     * 注册一个认证管理器对象到容器
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }


    /**
     * 密码编码对象(密码不进行加密处理)
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        //return NoOpPasswordEncoder.getInstance(); //密码不加密

        return new BCryptPasswordEncoder(); //密码使用bcrypt加密
    }

    @Autowired
    private PasswordEncoder passwordEncoder;


    /**
     * 处理用户名和密码验证事宜
     * 1)客户端传递username和password参数到认证服务器
     * 2)一般来说,username和password会存储在数据库中的用户表中
     * 3)根据用户表中数据,验证当前传递过来的用户信息的合法性
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   

        //从数据库获取用户数据
        auth.userDetailsService( jdbcUserDetailsService ).passwordEncoder( passwordEncoder );




    }
}

Oauth2 server的配置类

package com.lagou.edu.oauth.config;

import com.netflix.discovery.converters.Auto;
import io.netty.util.internal.NoOpTypeParameterMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.sql.DataSource;


/**
 * FileName: OauthServerConfiger
 * Author:   xuan zongjun
 * Date:     2021/10/29 16:44
 * Description: 当前类为Oauth2 server的配置类(需要继承特定的父类AuthorizationServerConfigurerAdapter)
 */

@Configuration
@EnableAuthorizationServer // 开启认证服务器功能
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {

    @Autowired
    LagouAccessTokenConvertor lagouAccessTokenConvertor; //jwt的扩展信息

    @Autowired
    private AuthenticationManager authenticationManager;

    private String sign_key = "123456"; // jwt签名密钥

    @Autowired
    private DataSource dataSource;

    /**
     * 认证服务器最终是以api接口的方式对外提供服务(校验合法性并生成令牌、校验令牌等)
     * 那么,以api接口方式对外的话,就涉及到接口的访问权限,我们需要在这里进行必要的配置
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure( security );
        //相当于打开endpoints 访问接口的开关,这样的话后期我们能够访问接口
        security
                .allowFormAuthenticationForClients() //允许客户端表单认证
                .tokenKeyAccess( "permitAll()" )    // 开启端口/oauth/token_key的访问权限(允许)
                .checkTokenAccess( "permitAll()" ); // 开启端口/oauth/check_token的访问权限(允许)
              


    }

    /**
     * 客户端详情配置,
     * 比如: client_id,secret
     * 当前这个服务就如同QQ平台,拉勾网作为客户端需要qq平台进⾏登录授权认证等,提前需要到QQ平台注册,QQ平台会给拉勾网
     * 颁发client_id 等必要参数,表明客户端是谁
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure( clients );

        //2、客户信息存储在数据库
        // 从内存中加载客户端详情改为从数据库中加载客户端详情,默认表:oauth_client_details
        clients.withClientDetails( createJdbcClientDetailsService() );
    }

    @Bean
    public JdbcClientDetailsService createJdbcClientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService( dataSource );
        return jdbcClientDetailsService;
    }

    /**
     * 认证服务器是玩转token的,那么这个配置token令牌管理相关(token此时就是一个字符串,当下的token需要在服务器端存储,
     * 那么存储在哪里呢?都是在这里配置)
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure( endpoints );
        endpoints
                .tokenStore( tokenStore() )// 指定token的存储方法
                .tokenServices( authorizationServerTokenServices() ) // token服务的一个描述,可以认为是token生成细节的描述,比如有效时间多少等
                .authenticationManager( authenticationManager ) //认证管理器对象到容器
                .allowedTokenEndpointRequestMethods( HttpMethod.POST, HttpMethod.GET );


    }

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

    @Autowired
    PasswordEncoder passwordEncoder;


    /*
   该方法用于创建tokenStore对象(令牌存储对象)
   token以什么形式存储
   */
    public TokenStore tokenStore() {
        //return new InMemoryTokenStore();
        return new JwtTokenStore( jwtAccessTokenConverter() );
    }

    /**
     * 返回jwt令牌转换器(帮助我们生成jwt令牌的)
     * 在这里,我们可以把签名密钥传递进去给转换器对象
     *
     * @return
     */
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey( sign_key );// 签名密钥
        jwtAccessTokenConverter.setVerifier( new MacSigner( sign_key ) ); // 验证时使用的密钥,和签名密钥保持一致MacSigner 指定对称加密
        jwtAccessTokenConverter.setAccessTokenConverter( lagouAccessTokenConvertor );
        return jwtAccessTokenConverter;

    }


    /**
     * 该方法用户获取一个token服务对象(该对象描述了token有效期等信息)
     */
    public AuthorizationServerTokenServices authorizationServerTokenServices() {
        // 使用默认实现
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setSupportRefreshToken( true ); // 是否开启令牌刷新
        defaultTokenServices.setTokenStore( tokenStore() );

        //针对jwt令牌的添加
        defaultTokenServices.setTokenEnhancer( jwtAccessTokenConverter() );


        // 设置令牌有效时间(一般设置为2个小时)
        defaultTokenServices.setAccessTokenValiditySeconds( 200 ); // access_token 就是我们请求资源需要携带的令牌

        // 设置刷新令牌的有效时间
        defaultTokenServices.setRefreshTokenValiditySeconds( 259200 ); //3天
        return defaultTokenServices;
    }


}

 三、解决办法

因为springsecurity在最新版本升级后,默认把之前的明文密码方式给去掉了官方文档说明;
解决方式:

  1. 一般我们客户端账号密码不需要加密,所以在这里实现 .passwordEncoder(NoOpPasswordEncoder.getInstance()) 告诉security客户端密码不需要加密
  2. 使用BCryptPasswordEncoder将数据库中client密码加密

我的修改:

   @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure( security );
        //相当于打开endpoints 访问接口的开关,这样的话后期我们能够访问接口
        security
                .allowFormAuthenticationForClients() //允许客户端表单认证
                .tokenKeyAccess( "permitAll()" )    // 开启端口/oauth/token_key的访问权限(允许)
                .checkTokenAccess( "permitAll()" ) // 开启端口/oauth/check_token的访问权限(允许)
                .passwordEncoder( NoOpPasswordEncoder.getInstance() ); //设置 客户信息 client_secret  使用明文验证


    }


参考地址:

spring boot security 项目中Encoded password does not look like BCrypt报错解决 - 简书

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值