General documentation: Article directory Github: github.com/black-ant
A. The preface
In this article, we take a closer look at SpringSecurity and its OAuth2.0 process logic.
2. Easy to use
2.1 the Maven rely on
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<! - request packet -- -- >
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5. RELEASE</version>
</dependency>
Copy the code
2.2 configuration items
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler myAuthenctiationFailureHandler;
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
@Autowired
private UserService userService;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public AuthenticationManager authenticationManagerBean(DataSource dataSource) throws Exception {
OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
authenticationManager.setTokenServices(new DefaultTokenServices());
authenticationManager.setClientDetailsService(new JdbcClientDetailsService(dataSource));
return authenticationManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// This method is used for user authentication, where the memory user is added and permissions are specified
auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}
@Autowired
public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
for(AuthorizationServerConfigurer configurer : configurers) { configurer.configure(clientDetails); }}@Override
protected void configure(HttpSecurity http) throws Exception {
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
configure(configurer);
http.apply(configurer);
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String authorizeEndpointPath = handlerMapping.getServletPath("/oauth/authorize");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
if(! endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); }/ / PS: note that the request itself has a WebSecurityConfigurerAdapter, I custom overlay in the options here
http.authorizeRequests()
.antMatchers("/test/**").permitAll()
.antMatchers("/before/**").permitAll()
.antMatchers("/index").permitAll()
.antMatchers(authorizeEndpointPath).authenticated()
.antMatchers(tokenEndpointPath).fullyAuthenticated()
.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
.anyRequest().authenticated() // All other requests must be verified before access
.and()
.requestMatchers()
// .antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.formLogin()
.loginPage("/login") // Define the login page "/login" to allow access
.defaultSuccessUrl("/home") // Default to "list" after successful login
.successHandler(myAuthenticationSuccessHandler).failureHandler(myAuthenctiationFailureHandler).permitAll().and()
.logout() // The default "/logout" is allowed
.logoutSuccessUrl("/index")
.permitAll();
http.addFilterBefore(new BeforeFilter(), UsernamePasswordAuthenticationFilter.class);
http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}
@Override
public void configure(WebSecurity web) throws Exception {
// Resolve the issue of static resources being intercepted
web.ignoring().antMatchers("/**/*.js"."/lang/*.json"."/**/*.css"."/**/*.js"."/**/*.map"."/**/*.html"."/**/*.png");
}
protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
for(AuthorizationServerConfigurer configurer : configurers) { configurer.configure(oauthServer); }}}Copy the code
Resource Resource configuration
@Configuration
@EnableResourceServer
public class ResServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenStore(tokenStore).resourceId("resourceId");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers()
.antMatchers("/user"."/res/**")
.and()
.authorizeRequests()
.antMatchers("/user"."/res/**") .authenticated(); }}Copy the code
OAuthConfig exclusive property
@Configuration
@EnableAuthorizationServer
@Order(2)
public class OAuthConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
@Lazy
private AuthenticationManager authenticationManager;
@Bean
public TokenStore tokenStore(a) {
return new JdbcTokenStore(dataSource);
}
@Bean
public JdbcClientDetailsService jdbcClientDetailsService(a) {
return new JdbcClientDetailsService(dataSource);
}
@Bean
public ApprovalStore approvalStore(a) {
return new JdbcApprovalStore(dataSource);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices(a) {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(jdbcClientDetailsService());
}
// Check the token policy
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
security.tokenKeyAccess("isAuthenticated()");
security.checkTokenAccess("permitAll()");
}
// Master configuration information of OAuth2
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
// .approvalStore(approvalStore()).authenticationManager(authenticationManager) .authorizationCodeServices(authorizationCodeServices()) .tokenStore(tokenStore()); }}Copy the code
2.3 database
See the project
2.4 Usage Mode
Request way
http://localhost:8080/security/oauth/authorize? response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=app
https://www.baidu.com/?code=jYgDO3
Copy the code
AccessToken
var settings = {
"url": "http://localhost:8080/security/oauth/token"."method": "POST"."timeout": 0."headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"data": {
"grant_type": "authorization_code"."client_id": "client"."client_secret": "secret"."code": "CFUFok"."redirect_uri": "http://www.baidu.com"}}; $.ajax(settings).done(function (response) {
console.log(response);
});
/ / fail
{
"error": "invalid_grant"."error_description": "Invalid authorization code: CFUFok"
}
/ / success
{
"access_token": "c0955d7f-23fb-4ca3-8a52-c715867cbef2"."token_type": "bearer"."refresh_token": "55f53af0-1133-46dc-a32d-fbb9968e5938"."expires_in": 7199."scope": "app"
}
Copy the code
check Token
var settings = {
"url": "http://localhost:8080/security/oauth/check_token? token=c0955d7f-23fb-4ca3-8a52-c715867cbef2"."method": "GET"."timeout": 0}; $.ajax(settings).done(function (response) { console.log(response); });/ / return
{
"aud": [
"resourceId"]."exp": 1618241690."user_name": "gang"."client_id": "client"."scope": [
"app"]}Copy the code
3. Source parsing
3.1 the base class
TokenStore
TokenStore is an interface, and being an interface means that it can be completely customized in use
public interface TokenStore {
// Obtain an OAuth2Authentication from the OAuth2AccessToken object
OAuth2Authentication readAuthentication(OAuth2AccessToken token);
OAuth2Authentication readAuthentication(String token);
// Persistently associate OAuth2AccessToken and OAuth2Authentication
void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);
// Fetch and remove OAuth2AccessToken
OAuth2AccessToken readAccessToken(String tokenValue);
void removeAccessToken(OAuth2AccessToken token);
OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
// OAuth2RefreshToken direct operation
void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication);
OAuth2RefreshToken readRefreshToken(String tokenValue);
OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token);
void removeRefreshToken(OAuth2RefreshToken token);
// Remove the access token with the refresh token. This method is used to control the number of tokens
void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken);
// Client ID Query token
Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName);
Collection<OAuth2AccessToken> findTokensByClientId(String clientId);
}
Copy the code
Let’s take a look at the main TokenStore implementation classes, which provide the following implementations by default:
InMemoryTokenStore
- As you can see from the name, this class puts the Token in memory,
- If you click on it, you can see that there’s a lot of ConcurrentHashMap in the class, to make it safe for multithreaded access
// The only special thing is that it is not a set, but a set for each business. This is actually equivalent to the processing idea of dividing the database into tables
C- InMemoryTokenStore
- private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore
- private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore
- private final ConcurrentHashMap<String, Collection<OAuth2AccessToken>> userNameToAccessTokenStore
// Internal category TokenExpiryPSC- TokenExpiry implements Delayed ? - Delayed is the interface for delaying processing, which is used to determine whether the Token has expired -private final long expiry;
- private final String value;
Copy the code
JdbcTokenStore
JdbcTokenStore is a viable solution, but it is not the optimal solution. Database processing poses challenges for high concurrency and high performance
// Select * from 'Set' where 'Set' = 'Set'
private static final String DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT = "insert into oauth_access_token (toke....."
private String insertAccessTokenSql = DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT;
// Key point 2: Use JDBCTemplate, meaning regular Spring configuration will do
private final JdbcTemplate jdbcTemplate;
Copy the code
RedisTokenStore
Redis storage Token is a common storage method. Generally, it is the preferred solution. JDBC is used only when the environment cannot be used
- Colons distinguish folders
- RedisConnectionFactory requires the Redis package
- JdkSerializationStrategy Serialization strategy
-
Serialization is the most important area of concern, and some monitoring frameworks may conflict with serialization
-
There are two other special implementation classes that are not a way to persist
JwkTokenStore
- Support is provided for JSON Web signature (JWS) validation of JSON Web token (JWT) using JSON Web Key (JWK)
- The token library implementation is dedicated to the resource server with the sole responsibility of decoding the JWT and verifying its signature (JWS) with the corresponding JWK
- As you can see from this introduction, it is used for the resource server and its main purpose is transformation, so it is not difficult to click on it and see that there is an object inside for the underlying call
private finalTokenStore delegate: Use this object to handle the underlying way// The common constructor
public JwkTokenStore(String jwkSetUrl)
public JwkTokenStore(List<String> jwkSetUrls)
public JwkTokenStore(String jwkSetUrl, AccessTokenConverter accessTokenConverter)
public JwkTokenStore(String jwkSetUrl, JwtClaimsSetVerifier jwtClaimsSetVerifier)
public JwkTokenStore(String jwkSetUrl, AccessTokenConverter accessTokenConverter,JwtClaimsSetVerifier jwtClaimsSetVerifier)
public JwkTokenStore(List<String> jwkSetUrls, AccessTokenConverter accessTokenConverter,JwtClaimsSetVerifier jwtClaimsSetVerifier)
Copy the code
Extended information:
- JWK RFC: tools.ietf.org/html/rfc751…
- JWT RFC: tools.ietf.org/html/rfc751…
- JWS RFC: tools.ietf.org/html/rfc751…
JwtTokenStore
This object is actually a new system, a JWT implementation of tokens, not just a way to store them
- private JwtAccessTokenConverter jwtTokenEnhancer;
- private ApprovalStore approvalStore;
- JdbcApprovalStore
- TokenApprovalStore
- InMemoryApprovalStore
Copy the code
- You can see that a member variable for transformation and a store object are provided
- The important thing to note here is that he only reads from the token itself. It’s not really storage, it never persists anything.
- Because, JWT itself stores the data
3.2 Event Type and Processing
Events are used to push, the main use of DefaultAuthenticationEventPublisher, let’s take a look at him
DefaultAuthenticationEventPublisher
The constructor gives you a rough idea of the event type
public DefaultAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
addMapping(BadCredentialsException.class.getName(),AuthenticationFailureBadCredentialsEvent.class);
addMapping(UsernameNotFoundException.class.getName(),AuthenticationFailureBadCredentialsEvent.class);
addMapping(AccountExpiredException.class.getName(),AuthenticationFailureExpiredEvent.class);
addMapping(ProviderNotFoundException.class.getName(),AuthenticationFailureProviderNotFoundEvent.class);
addMapping(DisabledException.class.getName(),AuthenticationFailureDisabledEvent.class);
addMapping(LockedException.class.getName(),AuthenticationFailureLockedEvent.class);
addMapping(AuthenticationServiceException.class.getName(),AuthenticationFailureServiceExceptionEvent.class);
addMapping(CredentialsExpiredException.class.getName(),AuthenticationFailureCredentialsExpiredEvent.class);
addMapping( "org.springframework.security.authentication.cas.ProxyUntrustedException", AuthenticationFailureProxyUntrustedEvent.class); } M- publishAuthenticationSuccess ? M - release certification success events - publishAuthenticationFailure - AbstractAuthenticationEvent event = constructor. NewInstance (authentication, exception); ? - build a AbstractAuthenticationEvent - applicationEventPublisher. PublishEvent (event)? - M - setAdditionalExceptionMappings release event? - Set additional exceptions to event mappings. They are automatically merged with the default exception of the event mapping defined by the ProviderManagerCopy the code
3.3 Service processing class
ResourceServerTokenServices interface
public interface ResourceServerTokenServices {
OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException;
OAuth2AccessToken readAccessToken(String accessToken);
}
Copy the code
DefaultTokenServices
Default Token handling class, see nothing special, mainly calls to TokenStore
RemoteTokenServices
- Query the /check_token endpoint to get the contents of the access token. If the endpoint returns a 400 response, this indicates that the token is invalid.
C- RemoteTokenServices
F- privateRestOperations restTemplate; - Simply call the check_token interface from this object to query the token information - note that, unlike the local class, this method should be intended for the current OAuth service as an SPCopy the code
3.4 Token Management system
TokenGranter is an interface that has a bunch of implementation classes,
One of the most common should be AuthorizationCodeTokenGranter and ImplicitTokenGranter, RefreshTokenGranter
C - AuthorizationCodeTokenGranter M - getOAuth2Authentication - Paramters in Code, And sentenced to empty - > InvalidRequestException - authorizationCodeServices. ConsumeAuthorizationCode (authorizationCode) : Through the Code to obtain OAuth2Authentication - whether redirectUri and clientId exist - > RedirectMismatchException/InvalidClientExceptionCopy the code
3.5 Token Conversion system
C- DefaultAccessTokenConverter ? - Default Token processing system, let's take a look at the main M- convertAccessToken? - As you can see, there are different switches throughout the conversion logic to determine which ones to displayCopy the code
3.6 Configuration Details
OAuth provides automatic configuration of the OAuth protocol. OAuth provides automatic configuration of the OAuth protocol. OAuth contains two classes:
- Implement User authentication
- AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter
- Implement Resource authentication
-
ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered
-
// ResourceServerConfiguration
F- private TokenStore tokenStore; // Token management implementation
F- private AuthenticationEventPublisher eventPublisher; // Event publishing
F- private Map<String, ResourceServerTokenServices> tokenServices; // Token Service collection
F- private ApplicationContext context;
F- privateList<ResourceServerConfigurer> configurers = Collections.emptyList(); ? - The collection here can be used to customize the ResourceServerConfigurer class F-privateAuthorizationServerEndpointsConfiguration endpoints; ? Psc-notoauthrequestmatcher M- configure(HttpSecurity HTTP)? - the core configuration method, mainly produces a ResourceServerSecurityConfigurer in HttpSecurity? - This is actually a clone of the current object to HttpSecurity, not a reference - the previous steps are: Configure tokenServices + tokenStore + eventPublisher - and then find an interesting place: Structurally, this should be an application of decoratorsfor(ResourceServerConfigurer configurer : configurers) { configurer.configure(resources); } - The next steps to start configuring HttpSecurity itself are -authenticationProvider: AnonymousAuthenticationProvider - exceptionHandling - accessDeniedHandler - sessionManagement : Session management - sessionCreationPolicy - handling CSRF across domains - adding requestMatcher - and then finding another interesting place where both parties hold objects to each otherfor (ResourceServerConfigurer configurer : configurers) {
configurer.configure(http);
}
// AuthorizationServerSecurityConfiguration
protected void configure(HttpSecurity http) throws Exception {
/ / same as above, but to build a new AuthorizationServerSecurityConfigurer in HttpSecurity
AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();
//
FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping();
http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping);
// Decorators hold objects for more extended functionality
configure(configurer);
http.apply(configurer);
// Get the OAuth related interface and configure the corresponding permission requirements below
String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token");
String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key");
String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token");
if(! endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); }/ / a little... Nothing critical. It's all generic, right
http
.authorizeRequests()
.antMatchers(tokenEndpointPath).fullyAuthenticated()
.antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess())
.antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess())
.and()
.requestMatchers()
.antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.setSharedObject(ClientDetailsService.class, clientDetailsService);
}
Copy the code
4. Running logic
Having looked at the configuration logic, let’s look at the main operation logic:
4.1 Request Entry
Let’s start with the entry authorize of the request and see what path the request took to get here
Process started
Skipping a series of Invoke and MVC processes to find the FilterChainProxy class, you can probably see that the OAuth protocol filters requests within the Filter overall process
Let’s find out which filter class is blocking requests to the login page:
Step 1 : SecurityContextPersistenceFilter
This is mainly done in finally after the Filter chain is executed
Step 2 : BasicAuthenticationFilter
As you can see, the code does these things
C01- BasicAuthenticationFilter
String header = request.getHeader("Authorization"); ? - Basic Z2FuZzoxMjM0NTY=// If the header is empty, the Filter is continued
if (header == null| |! header.toLowerCase().startsWith("basic ")) {
chain.doFilter(request, response);
return;
}
// If the authentication information exists
- String[] tokens = extractAndDecodeHeader(header, request);
- String username = tokens[0];
- new UsernamePasswordAuthenticationToken(username, tokens[1]); ? - build a UsernamePasswordAuthenticationToken -... (PS: the logic Filter detailed said, don't repeatedly said) - SecurityContextHolder. GetContext () setAuthentication (authResult);Copy the code
The logic of Filter has already been explained before, so I won’t go into too much detail here
Other extended Filter
TokenEndpointAuthenticationFilter
Optional authentication filter for TokenEndpoint. It is located in the client’s another filter downstream (usually BasicAuthenticationFilter), if the request is also contains the user credentials, it would be for Spring SecurityContext create a OAuth2Authentication.
If you use this filter, the Spring security context will contain an OAuth2Authentication encapsulation (as an authorization request), the form parameters that enter the filter, and the client ID that authenticates from the already authenticated client. And the authenticated user token extracted from the request and authenticated using the authentication manager.
OAuth2AuthenticationProcessingFilter
Pre-authentication filters for OAuth2 protected resources.
Extracted from the incoming request a OAuth2 token, and use it with OAuth2Authentication (if used) with OAuth2AuthenticationManager filled with Spring security context.
4.2 Interface Details
Note that by the time of this interface, authentication has actually been completed. See Filter above for the interception process. This part only analyzes the internal process
Interface 1: Authorize
http://localhost:8080/security/oauth/authorizeC06- AuthorizationEndpoint M601- authorize P- Map<String, Object> model P- Map<String, String> parameters : SessionStatus P- Principal Principal: Because, in fact, already finished certification, so can get Principal - getOAuth2RequestFactory () createAuthorizationRequest (parameters); ? - through the parameters generated a AuthorizationRequest, the flow in the process of the object for authentication object - AuthorizationRequest. GetResponseTypes () : Get Set<String> of tResponseTypes? - if the collection type correctly - > UnsupportedResponseTypeException? - TODO: Why set? - authorizationRequest. GetClientId () : check ClientId exists - > InvalidClientException - principal. IsAuthenticated () : Check whether the certification - > InsufficientAuthenticationException - authorizationRequest. SetRedirectUri (resolvedRedirect) : to generate and set up the redirection address? - note that this address at this time also without a Code - oauth2RequestValidator. ValidateScope (authorizationRequest, client)? - check whether the scope of the current client contains the current request - userApprovalHandler. CheckForPreApproval (authorizationRequest, (Authentication) principal)? - TODO - userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal) ? - whether the request has been approved by the end user (or other processes) - getAuthorizationCodeResponse (authorizationRequest, (Authentication) principal)? M602? ResponseType = code - ResponseType = token when the final processing logic: M605 M602 getAuthorizationCodeResponse - getSuccessfulRedirect(authorizationRequest,generateCode(authorizationRequest, authUser)):M603 M603- getSuccessfulRedirect - Map<String, String> query =new LinkedHashMap<String, String>();
- query.put("code", authorizationCode); ? - insert the Code - String state = authorizationRequest. GetState (); -if(state ! =null) query.put("state", state); ? - Insert State M604 -Generatecode -oAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest); - OAuth2Authentication combinedAuth =newOAuth2Authentication(storedOAuth2Request, authentication); - String code = authorizationCodeServices.createAuthorizationCode(combinedAuth); ? - core method, pay attention to the AuthorizationCodeServices here is an interface, means that the object can be custom implementation? - the generated classes here is RandomValueStringGenerator// Add the Token modeWhat happens here when it comes to using Implicit mode for authentication? M605- getImplicitGrantResponse(authorizationRequest) - TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(authorizationRequest,"implicit"); - OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest); - OAuth2AccessToken accessToken = getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request); ? - This method generates the last Token, and raises an exception if it is null - getTokenGranter().grant()"implicit".new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
Copy the code
Interface 2: AccessToken Interface/oAuth /token
C07- TokenEndpoint ? - The Token processing is mainly concentrated in the class, which provides the POST and GET request capabilities2M701- postAccessToken(Principal Principal,Map<String, String> parameters) - Determine whether authentication has been performed - getClientDetailsService().loadClientByClientid (clientId)? - Get clientId first, then get a ClientDetails - getOAuth2RequestFactory().createToKenRequest (Parameters, authenticatedClient)? - Build a TokenRequest - check the Client ID, and then check the ClientDetails Scope - check whether the GrantType is reasonable, can not be empty, Cannot be implicit - tokenRequest. SetScope (OAuth2Utils parseParameterList (parameters) get (OAuth2Utils. SCOPE))); ? - process - getTokenGranter RefreshToken type () grant (tokenRequest. GetGrantType (), tokenRequest)? CodeToken generation logic - getResponse(token) : Generates a Response objectCopy the code
Interface 3: CheckTokenEndpoint – / oAuth /check_token
C08- CheckTokenEndpoint M801- checkToken - OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value); ? - Obtain the OAuth2AccessToken object using the Token. - OAuth2Authentication Authentication = resourceServerTokenServices.loadAuthentication(token.getValue()); ? - if OAuth2AccessToken exist and not expired, obtain OAuth2Authentication - accessTokenConverter. ConvertAccessToken (token, authentication) ? - Returns user informationCopy the code
Client Core processing
The Client is also a very core concept in OAuth, and not surprisingly, Client validation is still handled through filters
C- ClientCredentialsTokenEndpointFilter
M- attemptAuthentication
- String clientId = request.getParameter("client_id");
- String clientSecret = request.getParameter("client_secret"); - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); ? - if the certification, it is returned directly (PS: this is the Client authentication) - UsernamePasswordAuthenticationToken authRequest =newUsernamePasswordAuthenticationToken(clientId,clientSecret); ? - build a UsernamePasswordAuthenticationToken for authentication Client - ihs. GetAuthenticationManager () authenticate (authRequest)// ProviderManager is then called for authentication processingC-providermanager m-authenticate for-getproviders () -result = provider.authenticate(authentication) : Completes authentication// DaoAuthenticationProvider
Copy the code
As you can see, the Client is completely authenticated as a user, and the whole system is universal
Service processing: OAuth2ErrorHandler Exception handling
TODO
AuthenticationProvider architecture
The overall architecture of the AuthenticationProvider, which is the main body of authentication, is extremely large
TODO
4.3 Other important tool classes
DefaultStateKeyGenerator
The default state build tool
private RandomValueStringGenerator generator = newRandomValueStringGenerator(); ? - Use random numbersCopy the code
4.4 exception class
- OAuth2AccessDeniedException
- We usually want a 403 when access is denied, but we want the same treatment as all other OAuth2Exception types
- UserApprovalRequiredException
- License exception
- UserRedirectRequiredException
- This exception is thrown, allowing the token to be redirected
- AccessTokenRequiredException
- JwkException
4.5 supplementary class
OAuth2RestTemplate
OAuth2’s custom RestTemplate makes OAuth2 authenticated Rest requests using the credentials of the provided resources
// This includes some customizations related to OAuthAppendQueryParameter: builds the parameter of the token request. - acquireAccessToken: builds an OAuth2AccessToken?? - getAccessToken: Access token to get or update the current context if necessary Create a request, can call DefaultOAuth2RequestAuthenticator to generate a Token C - DefaultOAuth2RequestAuthenticator? - Generate an OAuth2Request-authorization Bearer via AccessToken....Copy the code
ProviderDiscoveryClient
OAuth2 should also support OIDC. This class is used to discover clients of providers configured according to the OIDC specification
public ProviderConfiguration discover(a) {
// Initiate a request
Map responseAttributes = this.restTemplate.getForObject(this.providerLocation, Map.class);
ProviderConfiguration.Builder builder = new ProviderConfiguration.Builder();
// Obtain OIDC information
builder.issuer((String)responseAttributes.get(ISSUER_ATTR_NAME));
builder.authorizationEndpoint((String)responseAttributes.get(AUTHORIZATION_ENDPOINT_ATTR_NAME));
if (responseAttributes.containsKey(TOKEN_ENDPOINT_ATTR_NAME)) {
builder.tokenEndpoint((String)responseAttributes.get(TOKEN_ENDPOINT_ATTR_NAME));
}
if (responseAttributes.containsKey(USERINFO_ENDPOINT_ATTR_NAME)) {
builder.userInfoEndpoint((String)responseAttributes.get(USERINFO_ENDPOINT_ATTR_NAME));
}
if (responseAttributes.containsKey(JWK_SET_URI_ATTR_NAME)) {
builder.jwkSetUri((String)responseAttributes.get(JWK_SET_URI_ATTR_NAME));
}
return builder.build();
}
Copy the code
// You can basically see these OIDC attributes
private static final String PROVIDER_END_PATH = "/.well-known/openid-configuration";
private static final String ISSUER_ATTR_NAME = "issuer";
private static final String AUTHORIZATION_ENDPOINT_ATTR_NAME = "authorization_endpoint";
private static final String TOKEN_ENDPOINT_ATTR_NAME = "token_endpoint";
private static final String USERINFO_ENDPOINT_ATTR_NAME = "userinfo_endpoint";
private static final String JWK_SET_URI_ATTR_NAME = "jwks_uri";
Copy the code
4.6 Extended Service Customization
Our ultimate goal is to know which nodes can be extended:
Security can be extended in the following ways:
- Where the interface is used
- TokenStore
- ResourceServerTokenServices
- ClientDetailsService
- AuthorizationServerConfigurer
- Where the switch is used
- Extend Filter to listen on AccessToken
- OAuth2AuthenticationFailureEvent
- OAuth2ClientAuthenticationProcessingFilter
- Where the Set method is provided
TODO: it’s useless to just talk about it, I’ll try TODO a Demo later
conclusion
On the whole, I went a general way, but there are still some scattered points that I don’t want to go, and my energy is limited. If it is involved, I will perfect it
Reference and thanks
Blog.csdn.net/weixin_4184…