Yesterday, Songko and his friends talked about the authorization code mode in OAuth2. I wrote a very detailed case from beginning to end to share the use of authorization code mode with my friends.

Why is there no demo code for the other three authorization modes? Learn to learn the whole set! This is not, Songge hurriedly put the other three authorization mode of the code whole, for small partners reference.

For today’s case, I will not write from the beginning, we will modify the code on the basis of the previous article, if you have not read the previous several articles in this series, I suggest you must read it first, otherwise you may not understand this article:

  • Do micro services around OAuth2, songge also come and you pull a pull
  • This case to write out, but also afraid to tell the interviewer do not understand OAuth2 login process?

Next, songko directly on the code, I will not repeat the process of various authorization modes, you can refer to the first article in this series.

By the way, you can still download the source code at the end of this article.

1. Simplified mode

To support the simplified pattern, it’s actually quite simple.

First, we added the following configuration in the authorization server to support the simplified mode:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("javaboy")
            .secret(new BCryptPasswordEncoder().encode("123"))
            .resourceIds("res1")
            .authorizedGrantTypes("refresh_token"."implicit")
            .scopes("all")
            .redirectUris("http://localhost:8082/index.html");
}
Copy the code

Note that we only need to add Implicit to authorizedGrantTypes to support the simplified schema.

After the configuration, restart auth-server.

Next we configure the resource server. Because there is no server in the simplified mode, we can only request the data on the resource server through JS, so the resource server needs to support cross-domain, we modify the following two places to support cross-domain:

@RestController
@CrossOrigin(value = "*")
public class HelloController {
    @GetMapping("/hello")
    public String hello(a) {
        return "hello";
    }
    @GetMapping("/admin/hello")
    public String admin(a) {
        return "admin"; }}Copy the code

First add the @crossorigin annotation to Controller to support cross-domain, then configure Spring Security to support cross-domain:

@Override
public void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .anyRequest().authenticated()
            .and()
            .cors();
}
Copy the code

After the configuration, restart the user-server.

Next, let’s configure third-party applications:

First we modify the index.html page:

<body>Hello, jiangnan a little rain!<a href="http://localhost:8080/oauth/authorize? client_id=javaboy&response_type=token&scope=all&redirect_uri=http://localhost:8082/index.html">Third-party Login (Simplified mode)</a>

<div id="div1"></div>
<script>
    var hash = window.location.hash;// Extract the parameter in the format #access_token= 9fda1800-3b57-4d32-AD01-05FF700d44cc&token_type = BEARer&expires _in=7199
    if (hash && hash.length > 0) {
        var params = hash.substring(1).split("&");
        var token = params[0].split("=");//[access_token,9fda1800-3b57-4d32-ad01-05ff700d44cc]
        $.ajax({
            type: 'get'.headers: {
                'Authorization': 'Bearer ' + token[1]},url: 'http://localhost:8081/admin/hello'.success: function (data) {$("#div1").html(data)
            }
        })
    }
</script>
</body>
Copy the code

The response_type value is token, indicating that the authorization code is returned directly. Other parameters remain unchanged.

So that when the user login successfully, after will be automatically redirected to the http://localhost:8082/index.html page, and add an anchor point parameters, like the following:

http://localhost:8082/index.html#access_token=9fda1800-3b57-4d32-ad01-05ff700d44cc&token_type=bearer&expires_in=1940
Copy the code

So next, we extract the parameter after # in JS and further parse out the access_token value.

With the value of the access_token, we send an Ajax request, place the access_token in the request header, and when the request succeeds, place the requested data in a div.

This is what we call a simplified model.

After configuration is complete, start the client – app, visit http://localhost:8082/index.html page, user authorization, will be automatically redirected to the page, display effect is as follows:

The full code can be downloaded at the end of this article.

2. Password mode

Password mode requires the user to directly enter the user name and password on the third-party application to log in. Let’s take a look.

Note that the following code builds on the authorization code pattern from the previous article.

First, auth-server is modified to support password mode:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("javaboy")
            .secret(new BCryptPasswordEncoder().encode("123"))
            .resourceIds("res1")
            .authorizedGrantTypes("password"."refresh_token")
            .scopes("all")
            .redirectUris("http://localhost:8082/index.html");
}
Copy the code

Everything else remains the same here, except the addition of password mode to authorizedGrantTypes.

Since the user logs in after using password mode, we need to configure an AuthenticationManager, again in the AuthorizationServer class, as follows:

@Autowired
AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints
            .authenticationManager(authenticationManager)
            .tokenServices(tokenServices());
}
Copy the code

Note that the authorization code mode, we configure AuthorizationCodeServices don’t need it, now replaced by the authenticationManager.

So where does this authenticationManager instance come from? This needs to be provided in the Spring Security configuration, which Songo explained several times in the previous Spring Security tutorial series, so I won’t go over it again. Here’s the code. Add the following code to SecurityConfig:

@Override
@Bean
public AuthenticationManager authenticationManagerBean(a) throws Exception {
    return super.authenticationManagerBean();
}
Copy the code

After the configuration, restart auth-server.

Next, configure client-app. First, add login function and modify index.html as follows:

<body>Hello, jiangnan a little rain!<form action="/login" method="post">
    <table>
        <tr>
            <td>User name:</td>
            <td><input name="username"></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input name="password"></td>
        </tr>
        <tr>
            <td><input type="submit" value="Login"></td>
        </tr>
    </table>
</form>
<h1 th:text="${msg}"></h1>
</body>
Copy the code

There’s nothing to say about this simple login feature.

Let’s look at the login interface:

@PostMapping("/login")
public String login(String username, String password,Model model) {
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("username", username);
    map.add("password", password);
    map.add("client_secret"."123");
    map.add("client_id"."javaboy");
    map.add("grant_type"."password");
    Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
    String access_token = resp.get("access_token");
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization"."Bearer " + access_token);
    HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
    ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/admin/hello", HttpMethod.GET, httpEntity, String.class);
    model.addAttribute("msg", entity.getBody());
    return "index";
}
Copy the code

In the login interface, after receiving a username and password, we send a POST request via the RestTemplate. Note that in the POST request, the value of grant_type is password. We can obtain the access_token returned by auth-server in the following format:

{access_token=02e3a1e1-925f-4d2c-baac-42d76703cae4, token_type=bearer, refresh_token=836d4b75-fe53-4e41-9df1-2aad6dd80a5d, expires_in=7199, scope=all}
Copy the code

As you can see, the token data returned is similar to the previous one.

After we extract the access_token, we then request the resource server and place the accessed data in the Model.

OK, after configuration is complete, start the client – app, visit http://localhost:8082/index.html page for testing. After authorization is completed, we can see the following contents on the project home page:

The full code can be downloaded at the end of this article.

3. Client mode

Client mode works for applications that don’t have a front end page, so I’m going to use a unit test to demonstrate this.

Note that the following code builds on the authorization code pattern from the previous article.

First modify auth-server to support client mode:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("javaboy")
            .secret(new BCryptPasswordEncoder().encode("123"))
            .resourceIds("res1")
            .authorizedGrantTypes("client_credentials"."refresh_token")
            .scopes("all")
            .redirectUris("http://localhost:8082/index.html");
}
Copy the code

Everything else remains the same here, except that the client_credentials mode is added to authorizedGrantTypes.

After the configuration, restart auth-server.

Next, in client-app, through unit testing, we write a piece of test code:

@Autowired
RestTemplate restTemplate;
@Test
void contextLoads(a) {
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("client_id"."javaboy");
    map.add("client_secret"."123");
    map.add("grant_type"."client_credentials");
    Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
    String access_token = resp.get("access_token");
    HttpHeaders headers = new HttpHeaders();
    headers.add("Authorization"."Bearer " + access_token);
    HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
    ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/hello", HttpMethod.GET, httpEntity, String.class);
    System.out.println(entity.getBody());
}
Copy the code

This code is the same as before, except that the request parameters are different. The value of the grant_type parameter is client_credentials. All else being the same, I won’t bore you with it.

So this unit test, when it’s done, I’m just going to print Hello, and I’m not going to take a screenshot.

The full code can be downloaded at the end of this article.

4. Refresh token

The next thing Songo wants to talk about is the common features of the four licensing models.

Taking the authorization code mode as an example, after we start auth-Server, in IntelliJ IDEA, we can see the interface exposed by the project:

So what are these interfaces for? Let’s take a look at the following table:

The endpoint meaning
/oauth/authorize This is the endpoint of the authorization
/oauth/token This is the endpoint that gets the token
/oauth/confirm_access The user confirms the endpoint of the authorization submission (that is, auth-server asks the user to authorize the submission address of that page)
/oauth/error Authorization error endpoint
/oauth/check_token Validates the endpoint of access_token
/oauth/token_key Provides the endpoint of the public key

At a glance. We’ve used most of these endpoints, and if we haven’t we’ll use them in the future, and I’ll explain them to you in more detail.

In addition to issuing tokens, the /oauth/token endpoint can also be used to refresh tokens. When we get the token, there is also a refresh_token, which is used to refresh the token.

I use Postman to make a simple refresh token request:

Note that the refresh_token parameter is required when refreshing. After refreshing, the old access_token will be invalid.

4. Other

Through the above three cases, combined with the previous article, Songgo through four complete code, to show the family the basic use of OAuth2 four authorization modes.

Here are four complete examples that you can download directly from Github:

Well, first say so much, if there is a harvest, must remember to point under the encouragement of songge ~

Case address: github.com/lenve/oauth…

Spring Boot: Spring Boot: Spring Boot: Spring Boot: Spring Boot: Spring Boot: Spring Boot: Spring Boot