SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器

一、项目环境配置

1.1 、JDK8

1.2、Spring Boot: 2.3.0.RELEASE

1.3、spring-cloud.version:Hoxton.SR4

二、代码

1、主要的依赖

1.1 starter-web

		
		
			org.springframework.boot
			spring-boot-starter-web
		

1.2 OAuth2

		
		
			org.springframework.cloud
			spring-cloud-starter-oauth2
		

1.3 springboot-test

		
		
			org.springframework.boot
			spring-boot-starter-test
			test
			
				
					org.junit.vintage
					junit-vintage-engine
				
			
		

1.4 完整的pom.xml文件



	4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.3.0.RELEASE
		 
	
	cn.iotspider
	oauth2
	0.0.1-SNAPSHOT
	oauth2
	Demo project for Spring Boot

	
		1.8
		Hoxton.SR4
	

	

		
		
			org.springframework.boot
			spring-boot-starter-web
		

		
		
			org.springframework.cloud
			spring-cloud-starter-oauth2
		

		
		
			org.springframework.boot
			spring-boot-starter-test
			test
			
				
					org.junit.vintage
					junit-vintage-engine
				
			
		

	

	
		
			
				org.springframework.cloud
				spring-cloud-dependencies
				${spring-cloud.version}
				pom
				import
			
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	


2、涉及到的文件

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第1张图片

2.1 pom.xml文件

2.2 WebConfig 配置SpringSecurity

package cn.iotspider.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
//对全部方法进行验证
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public PasswordEncoder passwordEncoder;

    /**
     * 配置用户登录验证服务
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       //未配置加密方式
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("123456")
                .roles("ADMIN");

        //配置了BCryptPasswordEncoder的加密方式
//        auth.inMemoryAuthentication()
//                .passwordEncoder(passwordEncoder())
//                .withUser("admin")
//                .password(passwordEncoder().encode("123456"))
//                .roles("ADMIN");
    }

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

}

configure(AuthenticationManagerBuilder auth)方法里如果使用的是未配置加密方式的登录认证,则在登录时控制台会报

There is no PasswordEncoder mapped for the id "null",这是springsecurity5中新增的加密方式

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第2张图片

到 package org.springframework.security.crypto.password 包里找到DelegatingPasswordEncoder类找到这个方法

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第3张图片

查看在该类里的extractId()方法,发现密码需要一个用{}包裹的前缀;

    private String extractId(String prefixEncodedPassword) {
        if (prefixEncodedPassword == null) {
            return null;
        } else {
            int start = prefixEncodedPassword.indexOf("{");
            if (start != 0) {
                return null;
            } else {
                int end = prefixEncodedPassword.indexOf("}", start);
                return end < 0 ? null : prefixEncodedPassword.substring(start + 1, end);
            }
        }
    }

再到该类的 matches(CharSequence rawPassword, String prefixEncodedPassword)


    public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
        if (rawPassword == null && prefixEncodedPassword == null) {
            return true;
        } else {
            String id = this.extractId(prefixEncodedPassword);
            PasswordEncoder delegate = (PasswordEncoder)this.idToPasswordEncoder.get(id);
            if (delegate == null) {
                return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);
            } else {
                String encodedPassword = this.extractEncodedPassword(prefixEncodedPassword);
                return delegate.matches(rawPassword, encodedPassword);
            }
        }
    }

可以看到使用extractId()提取出来的id查询PasswordEncoder接口的加密方式对象,所以我们从这可以知道,在设置密码时就需要在密码前使用{}包裹加密方式。例 "{bcrypt}" + passwordEncoder().encode("123456");

所以只要将 

.password("123456")

改为

.password("{bcrypt}" + passwordEncoder().encode("123456"))

即可;

    /**
     * 配置用户登录验证服务
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       //未配置加密方式
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("123456")
                .password("{bcrypt}" + passwordEncoder().encode("123456"))
                .roles("ADMIN");
    }
WebConfig中的完整代码:
package cn.iotspider.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
//对全部方法进行验证
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public PasswordEncoder passwordEncoder;

    /**
     * 配置用户登录验证服务
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       //未配置加密方式,控制台会报 There is no PasswordEncoder mapped for the id "null"
//        auth.inMemoryAuthentication()
//                .withUser("admin")
//                .password("123456")
//                .roles("ADMIN");


        //配置了BCryptPasswordEncoder的加密方式 (一),在passwordEncoder()中使用passwordEncoder()声明使用的加密方式
        auth.inMemoryAuthentication()
                .passwordEncoder(passwordEncoder())
                .withUser("admin")
                .password(passwordEncoder().encode("123456"))
                .roles("ADMIN");

        //配置了BCryptPasswordEncoder的加密方式 (二),在password()中使用{bcrypt}前缀声明使用的加密方式
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("{bcrypt}" + passwordEncoder().encode("123456"))
                .roles("ADMIN");
    }

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

}

加密方式(一)和(二)使用其中一种方式即可。

2.3 OAuth2ServerConfig配置OAuth2认证服务器

需要实现 AuthorizationServerConfigurerAdapter

完整代码:

package cn.iotspider.oauth2.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.PasswordEncoder;
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.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

import java.util.concurrent.TimeUnit;

@Configuration
@EnableAuthorizationServer
public class OAuth2ServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    public BCryptPasswordEncoder bCryptPasswordEncoder;

    @Bean
    public BCryptPasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 授权步骤相关的配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.inMemory()
                .withClient("client")
//                .secret("{bcrypt}"+bCryptPasswordEncoder.encode("secret"))
                .secret(bCryptPasswordEncoder.encode("secret"))
                .authorizedGrantTypes("authorization_code","client_credentials", "password", "refresh_token")
//                .authorizedGrantTypes("client_credentials", "password", "refresh_token")
                .scopes("all")
                .redirectUris("http://localhost:8080/callback")
                .accessTokenValiditySeconds(1200)
                .refreshTokenValiditySeconds(50000);
    }

    /**
     允许表单验证,浏览器直接发送post请求即可获取tocken;即允许使用POST方式发送获取token的请求
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
                "isAuthenticated()");
        oauthServer.allowFormAuthenticationForClients();
    }

}

配置好后启动springboot.

2.4登录获取授权码:http://localhost:8080/oauth/authorize?response_type=code&client_id=client&scope=all&redirect_uri=http://localhost:8080/callback

账号:admin

密码:123456

如果返回如下错误信息,通过信息可发现是无效的授权方式,也就是说没有配置使用授权码模式(Authorization code Grant)或 隐式授权模式(Implicit Grant),关于OAuth2的四种授权模式可阅读:OAuth2.0的四种授权模式,将第一个注释掉的authorizedGrantTypes放开并注释掉第二个authorizedGrantTypes,或者在authorizedGrantTypes中直接加入authorization_code即可。

OAuth Error
error="invalid_grant", error_description="A redirect_uri can only be used by implicit or authorization_code grant types."

重启后再次登录获取授权码,输入前面admin账号密码后,就能进入授权选择页面了

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第4张图片

选择Approve,再点击Authorize按钮后,跳转的地址就变成了之前设置的重定向地址了,同时返回了code授权码

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第5张图片

2.5获取token

使用PostMan,往http://localhost:8080/oauth/token发送post请求,http://localhost:8080/oauth/token?grant_type=authorization_code&code=y6jhAN&client_id=client&client_secret=secret&redirect_uri=http://localhost:8080/callback地址后面的都是请求需要携带的参数,发送后如果报错,且控制台又出现

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"这个错误,主要是因为OAuth2ServerConfig类中configure(ClientDetailsServiceConfigurer clients)方法里用的secret没有使用{}包裹的加密方式

secret(bCryptPasswordEncoder.encode("secret"))

改为

secret("{bcrypt}"+bCryptPasswordEncoder.encode("secret"))就可以了

后面就能获取到token等信息了

SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器_第6张图片

 

你可能感兴趣的:(SpringBoot集成SpringSecurity5和OAuth2 — 1、基于内存的OAuth2认证服务器)