Minimal implementation of Spring Security OAuth2 Provider

Not the client, but the server.

Version:
Spring Boot 1.4.3, Spring Security OAuth2 2.0.12

The open source Server / Client implementation of OAuth2.0 can be referred to here: https://oauth.net/code/ , where Spring Security OAuth2 is used to implement the most four authorization modes Commonly used: Authorization Code Grant.


For details, see the definition of the OAuth2.0 standard: https://tools.ietf.org/html/rfc6749#section-4.1 .

First of all, just to demonstrate the whole process of OAuth2.0, do the minimum implementation!

There are four URLs provided by Spring Security OAuth2 by default:
  • /oauth/authorize : 授权AuthorizationEndpoint
  • / oauth / token: TokenEndpoint
  • /oauth/check_token : Token verification CheckTokenEndpoint
  • /oauth/confirm_access : Authorization page WhitelabelApprovalEndpoint
  • /oauth/error : Error page WhitelabelErrorEndpoint

Related articles:
Spring Security OAuth2 Provider's minimal implementation
Spring Security OAuth2 Provider's database storage
Spring Security OAuth2 Provider's third-party login Simple demonstration
of Spring Security OAuth2 Provider's custom development
Spring Security OAuth2 Provider's integrated JWT

code is as follows:

pom.xml
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
</dependency>


Application.java
@SpringBootApplication
public class Application {
    
	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
	
}


Config.java
public class Config {
	
	public static final String OAUTH_CLIENT_ID = "oauth_client";
	public static final String OAUTH_CLIENT_SECRET = "oauth_client_secret";
	public static final String RESOURCE_ID = "my_resource_id";
	public static final String[] SCOPES = { "read", "write" };

	@Configuration
	@EnableAuthorizationServer
	static class OAuthAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
	    @Override
	    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
	        clients.inMemory()
	                .withClient(OAUTH_CLIENT_ID)
	                .secret(OAUTH_CLIENT_SECRET)
	                .resourceIds(RESOURCE_ID)
	                .scopes(SCOPES)
	                .authorities("ROLE_USER")
	                .authorizedGrantTypes("authorization_code", "refresh_token")
	                .redirectUris("http://default-oauth-callback.com")
	                .accessTokenValiditySeconds(60*30) // 30min
	                .refreshTokenValiditySeconds(60*60*24); // 24h
	    }
	}
	
	@Configuration
	@EnableResourceServer
	static class OAuthResourceConfig extends ResourceServerConfigurerAdapter {
		@Override
	    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
	        resources.resourceId(RESOURCE_ID);
	    }
		@Override
		public void configure(HttpSecurity http) throws Exception {
			http.authorizeRequests()
				.antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
	            .antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')");
		}
	}
	
	
	@Configuration
	@EnableWebSecurity
	static class SecurityConfig extends WebSecurityConfigurerAdapter {
	    @Override
	    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	        auth.inMemoryAuthentication()
	            .withUser("user").password("123").roles("USER")
	            .and()
	            .withUser("admin").password("123").roles("ADMIN");
	    }

	    @Override
	    protected void configure(HttpSecurity http) throws Exception {
	        http.csrf().disable();
        	http.authorizeRequests()
        		.antMatchers("/oauth/authorize").authenticated()
                .and()
                .httpBasic().realmName("OAuth Server");
	    }
	}

}


Controller.java
@RestController
public class Controller {

    @GetMapping("/api/get")
    public String get() {
        return "Hello World!";
    }

    @PostMapping("/api/post")
    public String post() {
    	return "POST process has finished.";
    }
    
    @GetMapping("/api/user")
    public Object get(HttpServletRequest req) {
    	SecurityContextImpl sci = (SecurityContextImpl) req.getSession().getAttribute("SPRING_SECURITY_CONTEXT");
    	if (sci != null) {
    		Authentication authentication = sci.getAuthentication();
    		if (authentication != null) {
    			return authentication.getPrincipal();
    		}
    	}
        return "none";
    }
    
}


Test.java
public class Test {

	public static void main(String[] args) {
		System.out.println(generate("oauth_client", "oauth_client_secret"));
	}

	private static String generate(String clientId, String clientSecret) {
		String creds = String.format("%s:%s", new Object[] { clientId, clientSecret });
		try {
			return "Basic " + new String(Base64.encode(creds.getBytes("UTF-8")));
		} catch (UnsupportedEncodingException e) {
			throw new IllegalStateException("Could not convert String");
		}
	}

}


[Run As] [Spring Boot App] After starting the server, you will see the following Log:
quote
Mapped "{[/oauth/authorize]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.authorize(java.util.Map<java.lang.String, java.lang.Object>,java.util.Map<java.lang.String, java.lang.String>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
Mapped "{[/oauth/authorize],methods=[POST],params=[user_oauth_approval]}" onto public org.springframework.web.servlet.View org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint.approveOrDeny(java.util.Map<java.lang.String, java.lang.String>,java.util.Map<java.lang.String, ?>,org.springframework.web.bind.support.SessionStatus,java.security.Principal)
Mapped "{[/oauth/token],methods=[POST]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
Mapped "{[/oauth/token],methods=[GET]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
Mapped "{[/oauth/check_token]}" onto public java.util.Map<java.lang.String, ?> org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint.checkToken(java.lang.String)
Mapped "{[/oauth/confirm_access]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint.getAccessConfirmation(java.util.Map<java.lang.String, java.lang.Object>,javax.servlet.http.HttpServletRequest) throws java.lang.Exception
Mapped "{[/oauth/error]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint.handleError(javax.servlet.http.HttpServletRequest)


(1) Authorization request (Get)

URL: http://localhost:8080/oauth/authorize?client_id=oauth_client&scope=read&response_type=code&state=rensanning&redirect_uri=http://default-oauth-callback.com

is enabled for /oauth/authorize HTTP Basic authentication is required, so you need to enter a password:

Enter the correct username and password (user/123) and display the authorization page:


select Approve and click the Authorize button, and automatically jump to http://default-oauth-callback.com?code= The code parameter value in the sdb6vF&state=rensanning


URL is the authorization code, which is a 6-digit random number. For details, see the source code: org.springframework.security.oauth2.common.util.RandomValueStringGenerator.generate()

! ! The 10-minute expiration of the authorization code has not been implemented yet! ! https://github.com/spring-projects/spring-security-oauth/issues/725

(2) Get token (Post)

URL: http://localhost:8080/oauth/token?grant_type=authorization_code&redirect_uri=http: //default-oauth-callback.com&code=sdb6vF

[/oauth/token] The default is HTTP Basic Auth (org.springframework.security.web.authentication.www.BasicAuthenticationFilter), so you need to provide the Base64 values ​​of clientId and clientSecret in the HTTP header. Specifically, you can execute Test.java to get it.
Authorization: Basic b2F1dGhfY2xpZW50Om9hdXRoX2NsaWVudF9zZWNyZXQ=


Through the following settings, the values ​​of clientId and clientSecret can be passed as parameters. For example: http://localhost:8080/oauth/token?grant_type=authorization_code&redirect_uri=http://default-oauth-callback.com&code=sdb6vF&client_id=oauth_client&client_secret=oauth_client_secret
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
  @Override
  public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
    oauthServer.allowFormAuthenticationForClients();
  }
}


return token info
quote
{
    "access_token": "1cc5ffbd-faac-4d20-afd9-b8531acd248e",
    "token_type": "bearer",
    "refresh_token": "5b319fed-5600-4ea2-8c4f-61f6e3ea6e41",
    "expires_in": 1631,
    "scope": "read"
}

The generated token is a UUID. For details, see the source code: org.springframework.security.oauth2.provider.token.DefaultTokenServices.createAccessToken()

(4) Accessing the API (Get)

directly accessing the API returns 401. URL: http://localhost:8080/api/get


is accessed through the access_token parameter. URL: http://localhost:8080/api/get?access_token=1cc5ffbd-faac-4d20-afd9-b8531acd248e Access


via http header parameter. URL: http://localhost:8080/api/get
Authorization: Bearer 1cc5ffbd-faac-4d20-afd9-b8531acd248e


*** @EnableResourceServer automatically adds OAuth2AuthenticationProcessingFilter
***! ! SpringBoot1.5 @EnableResourceServer and @EnableWebSecurity configured HttpSecurity has a sequence problem and requires special settings! ! Reference:
https://github.com/spring-projects/spring-security-oauth/issues/993#issuecomment-284430752
https://stackoverflow.com/questions/29893602/spring-security-form-logging-and-outh2-in-same-app

(5) Refresh token (Post)

URL: http://localhost:8080/oauth/ token?grant_type=refresh_token&refresh_token=5b319fed-5600-4ea2-8c4f-61f6e3ea6e41
Authorization: Basic b2F1dGhfY2xpZW50Om9hdXRoX2NsaWVudF9zZWNyZXQ=

Return new access_token:
quote
{
    "access_token": "3cbe70fc-753f-44ff-9bb4-0ba6bc3c9aab",
    "token_type": "bearer",
    "refresh_token": "5b319fed-5600-4ea2-8c4f-61f6e3ea6e41",
    "expires_in": 1800,
    "scope": "read"
}



The old Token can no longer be used:


access the API through the new Token:


Similarly, you can test the permission of the scope to write!

References:
http://projects.spring.io/spring-security-oauth/docs/oauth2.html
https://segmentfault.com/a/1190000010540911
http://ifeve.com/oauth2-tutorial-all/
http: //websystique.com/spring-security/secure-spring-rest-api-using-oauth2/
http://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f
https://murahun.jp/blog/20150920-01.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326267977&siteId=291194637