OAuth2.0
At a small level, it is often necessary to pass data as a separate project. Technically speaking, the client wants to access the data of the resource server, so it can access the data through the API provided by the resource server. Then the question is, is this safe? Of course not, because clients can masquerade and forge requested resources. So we need permission verification.
The OAuth2.0 protocol is designed to do just that.
OAuth2 is an open standard for authorization. The core idea is to authenticate users through various authentication methods (OAuth2 doesn’t care what methods), and issue tokens (tokens), so that third-party applications can use this token to access specified resources within a limited time and a limited scope. The main RFC specifications involved are RFC6749 (overall authorization framework),RFC6750 (token use),RFC6819 (threat model) these several, we generally need to know is RFC6749. There are four main ways to obtain tokens, namely, authorization code mode, simple mode, password mode and client mode. How to obtain tokens is beyond the scope of this article.
Reference: datatracker.ietf.org/doc/html/dr…
+--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+ Figure 2: Refreshing an Expired Access TokenCopy the code
(A) The client requests access tokens by authenticating with the authorization server and providing authorization. (B) The authorization server validates the client and validates the authorization and, if valid, issues the access token and refreshes the token. (C) The client issues a protected resource request to the resource server by presenting an access token. (D) The resource server validates the access token and, if valid, services the request. (E) Repeat steps (C) and (D) until the access token expires. If the client knows that the access token is expired, it will skip to step (G), otherwise it will issue another protected resource request. (F) The resource server will return an invalid token error because the access token is invalid. (G) The client requests a new access token by authenticating with the authorization server and rendering the refresh token. Client authentication requirements are based on client type and authorization server policy. (H) The authorization server validates the client and verifies the refresh token, and if it is valid, issues a new access token (which could be a new refresh token).
As you can see from reading the official documentation above, this process has clients, authorization servers, and resource servers.
We often hear that third-party authorization, such as jump alipay payment interface, jump wechat authorization login, JWT authentication, seems to be such a thing ah, so the security issues mentioned in the above example can be done in this way.
How to do, is generally direct to get the resources we want to do a permission check. First, every time the resource is accessed, the cookie is checked for refresh_token, and there is absolutely nothing for the first time. The user is redirected to account password login, which is the password mode mentioned above for obtaining the Token, and the login gets the access_token and refresh_token. Refresh_token is stored in cookie, and Access_token is generally cached in authentication server, and then access_token should be carried every time it is accessed. However, access_token is time-valid, so it will definitely expire when it is accessed again. Then we need to refresh the cookie with “refresh_token” and get the new “access_token” and “refresh_token”, and then store them in the cache and cookie. This gives you secure access to the resource.
Spring Security
Now that there is theoretical knowledge as the basis, then the real knowledge comes from practice. If you want to implement the OAuth2.0 process, you need to have a ready-made framework. You can’t write your own permission authentication system, so you choose Spring Security based on Spring Boot.
I checked the official documents and found that there are many ways to implement authentication. For example, the default is to use a separate account password for resource interception, and the password is in the console. Ok, then we will use Spring Boot to build a simple OAuth2.0 authentication system, Spring Security OAuth2.0 can configure AuthorizationServer and ResourceServer in one application or separately. I would put both the authorization server and the resource server in one project.
The authorization server is responsible for user login, authorization, and token authentication. The resource server is responsible for providing protected resources, but requires token authentication at the authorization server.
Build Spring Security OAuth2.0
I used Spring Boot 2.5.5
Pom.xml introduces Spring-Security-Oauth2, Web, hot boot devTools
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5. RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Copy the code
The doController.java Result returns json as a wrapper. If you want to pass String, change it. There are three MAIN API tests, plain no role, user and admin role.
@Controller
public class DoController {
@ResponseBody
@GetMapping("/admin/hello")
public Result admin(a){
return Result.ok("hello admin");
}
@ResponseBody
@GetMapping("/user/hello")
public Result user(a){
return Result.ok("hello user");
}
@ResponseBody
@GetMapping("/hello")
public Result hello(a){
return Result.ok("hello"); }}Copy the code
Then there are the three important Config configuration classes
AuthorizationServerConfig
This configuration class is used to configure the theoretical authorization server, including client name, client secret, which authorization types are available, token expiration time, resourceIds, and scopes for the service resource. AuthenticationManager for password granting. Allows clients to authenticate forms.
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client_id")
.authorizedGrantTypes("password"."refresh_token")
.accessTokenValiditySeconds(60 * 30) / / for 30 minutes
.refreshTokenValiditySeconds(60 * 60 * 2) / / 2 hours
.resourceIds("rid")
.scopes("all")
.secret(new BCryptPasswordEncoder().encode("123456"));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService); }}Copy the code
ResourceServerConfig
Configure the resource server corresponding to the authorization server and grant /user the role of user, which is the same as admin. The first level of path is set to anonymous users, that is, anyone who accesses it.
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("rid");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.antMatchers("/ * *").anonymous() .anyRequest().authenticated(); }}Copy the code
WebSecurityConfig
Set the authority manager superclass, user data, and password encoder. Build authentication in memory for simplicity. Anyone is allowed to use the /oauth/** path because the default API for obtaining tokens is under this path.
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected AuthenticationManager authenticationManager(a) throws Exception {
return super.authenticationManager();
}
@Bean
@Override
protected UserDetailsService userDetailsService(a) {
return super.userDetailsService();
}
@Bean
PasswordEncoder passwordEncoder(a) {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("admin")
.and()
.withUser("user").password(new BCryptPasswordEncoder().encode("123456")).roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/oauth/**")
.authorizeRequests()
.antMatchers("/oauth/**").permitAll() .and() .csrf() .disable(); }}Copy the code
test
This completes the OAuth2.0 functionality. We can test it:
First visit http://localhost:8080/hello, access to success.
Visit http://localhost:8080/user/hello, http://localhost:8080/admin/hello tip: complete authentication needed in the case of unauthorized access to this resource.
Reoccupy Postman tool, through the Api token simulation Post request: http://localhost:8080/oauth/token the following using the user permissions, along with the body of parameters: Username, password, grant_type, client_id, client_secret, scope
Yes, we have the access_token and refresh_token. Access_token = hello; access_token = hello; Add the access_token parameter through HTTP GET requests.
Try refresh_token again to refresh access_token. The body parameters need to be included: grant_type, refresh_token, client_id, client_secret
By setting the access token expiration time to 10s, you can refresh the token expiration time to 20s. It is concluded that:
- If the access token expires, the access is invalid and a new access token needs to be refreshed by calling the refresh token API.
- During the access token expiration time, a call to the refresh token API invalidates the previous access token.
- Calling the refresh token API results in a new access token and a self-refresh token.
- So when the refresh token expires, you must regain all tokens.
The default expiration time can be seen from the source code:
private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
Copy the code
The previous FODI project to deploy OneDrive lists showed that Microsoft’s Settings were typically accessed in 3 hours and refreshed in 3 months.
reference
Blog.csdn.net/zhoudingdin… Gitee.com/kdyzm/sprin… blog.kdyzm.cn/post/24