Is oauth2 single sign-on difficult to master? Just configure it

Single sign-on is a very common requirement in distributed systems.

The distributed system consists of many different subsystems, and when we use the system, we only need to log in once, so that other systems think that the user has already logged in, and there is no need to log in again. You can log in to Taobao Tmall, Alipay, Alibaba Cloud and other websites to experience single sign-on

Familiar with the oauth2 single sign-on function, Liu Bei will meet Zhuge Liang when he goes to the interview, like a fish in water

Today, Brother Heng wants to talk to you about Spring Boot+OAuth2 for single sign-on, and use the @EnableOAuth2Sso annotation to quickly realize the single sign-on function.
Source code download address

Without further ado, let’s start with the steps

project creation

Build the authorization server and the resource server together (of course, it is best to separate them, but there are also many enterprise products that are also put together, the purpose of this is convenience).

Today we need a total of three services:

Project port description
auth-server 9090 authorization server + resource server
client1 9091 subsystem 1
client2 9092 subsystem 2
auth-server is used to play the role of authorization server + resource server, client1 and client2 play the role of subsystem respectively, and client1 will wait for it in the future After the login is successful, we can also access client2, so that we can see the effect of single sign-on.

We create a Maven project named oauth2-sso as the parent project.

Unified Certification Center

Next, let's build a unified authentication center.

  • First, we create a module called auth-server, and add the following dependencies when creating it:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
  • After the project is successfully created, this module will play the role of authorization server + resource server, so we first add the @EnableResourceServer annotation to the startup class of this project, indicating that this is a resource server:
@SpringBootApplication
@EnableResourceServer
public class AuthServerApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(AuthServerApplication.class, args);
    }

}
  • Next, we configure the authorization server. Since the resource server and the authorization server are merged together, the configuration of the authorization server is much easier:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    
    
        security.checkTokenAccess("permitAll()");
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    
    
        clients.inMemory()
                .withClient("api")
                .secret(passwordEncoder.encode("123456"))
                .autoApprove(true)
                .redirectUris("http://127.0.0.1:9091/login", "http://127.0.0.1:9092/login")
                .scopes("user")
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("authorization_code");

    }
}

Here you only need to simply configure the client information, the configuration is very simple

For simplicity, the client's information configuration is based on memory

  • Next, let's configure Spring Security again:
@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Bean
    PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
    
    
        web.ignoring().antMatchers("/login.html", "/css/**", "/js/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.requestMatchers()
                .antMatchers("/login")
                .antMatchers("/oauth/authorize")
                .and()
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                .csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        auth.inMemoryAuthentication()
                .withUser("harry")
                .password(passwordEncoder().encode("admin123"))
                .roles("admin");
    }
}

Here is a rough overview:

1. First provide an instance of BCryptPasswordEncoder for password encryption and decryption.
Since I customized the login page, these static resources are allowed in WebSecurity.
In HttpSecurity, we release authentication-related endpoints, and configure the login page and login interface at the same time.
2. A memory-based user is provided in AuthenticationManagerBuilder (it can be modified here to load from the database).
3. There is another key point, because the resource server and the authorization server are together, so we need an @Order annotation to increase the priority of Spring Security configuration.

  • SecurityConfig and AuthServerConfig are both things that the authorization server needs to provide. If we want to split the authorization server and the resource server, we also need to provide an interface that exposes user information (if the authorization server and the resource server are separated, this interface will be provided by the resource server) :
@RestController
public class UserController {
    
    
    @GetMapping("/user")
    public Principal getCurrentUser(Principal principal) {
    
    
        return principal;
    }
}
  • Finally, we configure the project port in application.properties:

server.port=9091
In addition, Brother Heng prepared a login page in advance, as follows:

insert image description here

  • Copy the html, css, js, etc. related to the login page to the resources/static directory:
    insert image description here

  • This page is very simple, just a login form, I list the core parts:

<form action="/login" method="post">
    <div class="input">
        <label for="name">用户名</label>
        <input type="text" name="username" id="name">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="pass">密码</label>
        <input type="password" name="password" id="pass">
        <span class="spin"></span>
    </div>
    <div class="button login">
        <button type="submit">
            <span>登录</span>
            <i class="fa fa-check"></i>
        </button>
    </div>
</form>

Note that the action submission address should not be wrong.

After that, our unified authentication login platform is OK.

client creation

Next, let's create a client project, create a Spring Boot project named client1,

  • Add the following dependencies:
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

  • After the project is successfully created, let's configure Spring Security:
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
    
        http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
    }
}

This configuration is very simple, that is to say, all the interfaces in our client1 need to be authenticated before they can be accessed, and an @EnableOAuth2Sso annotation is added to enable the single sign-on function.

  • Next, we will provide another test interface in client1:
@RestController
public class UserController {
    
    
    @Value("${server.port}")
    private String port;
    @GetMapping("/getLogin")
    public String getLogin() {
    
    

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return "当前登录的用户是"+authentication.getName() + Arrays.toString(authentication.getAuthorities().toArray())+",登录端口:"+port;
    }
}

This test interface returns the name and role information of the currently logged in user, and the service port number.

  • Next, we need to configure oauth2 related information in the application.properties of client1:
security.oauth2.client.client-secret=123456
security.oauth2.client.client-id=api
security.oauth2.client.user-authorization-uri=http://127.0.0.1:9090/oauth/authorize
security.oauth2.client.access-token-uri=http://127.0.0.1:9090/oauth/token
security.oauth2.resource.user-info-uri=http://127.0.0.1:9090/user

server.port=9091

server.servlet.session.cookie.name=client1

The configuration here is also familiar, let's take a look:

client-secret is the client secret.
client-id is the client id.
user-authorization-uri is the endpoint for user authorization.
access-token-uri is the endpoint to get the token.
user-info-uri is the interface for obtaining user information (obtained from the resource server).
Finally, configure the port and give the cookie a name.
After that, our client1 configuration is complete.

In the same way, let's configure another client2. Client2 is exactly the same as client1, except that the name of the cookie is different (you can choose it at will, but it can be different).

test

Next, we start auth-server, client1 and client2 respectively. First, we try to go to the getLogin interface in client1. At this time, it will automatically jump to the unified authentication center
and enter the user name: harry, password: admin123

After successful login, it will automatically jump back to the hello interface of client1, as follows:

insert image description here

After client1 logs in, we go to visit client2 again, and find that there is no need to log in, and we can directly access:
insert image description here

OK, after that, our single sign-on is successful.

Process Analysis

Finally, let me go over an execution process of the above code with my friends:

1. First, we go to access the /getLogin interface of client1, which requires login to access, so our request is intercepted, after interception, the system will redirect us to the /login interface of client1, which allows us to Go to login.

insert image description here

2. When we access the login interface of client1, since we have configured the @EnableOAuth2Sso annotation, this operation will be intercepted again, and the single sign-on interceptor will automatically initiate a request to obtain authorization according to our configuration in application.properties code:

insert image description here

3. The request sent in the second step is to request something on the auth-server service. Of course, this request cannot avoid the need to log in first, so it is redirected to the login page of the auth-server again, which is the unified authentication you see center.
insert image description here

4. After the unified authentication center completes the login function, it will continue to execute the second step of the request, and at this time the authorization code can be successfully obtained.
insert image description here
insert image description here

5. After obtaining the authorization code, it will be redirected to the login page of our client1 at this time, but in fact our client1 does not have a login page, so this operation will still be intercepted, and the intercepted address at this time contains authorization code, take the authorization code, and initiate a request to the auth-server in the OAuth2ClientAuthenticationProcessingFilter class, and you can get the access_token.
insert image description here

6. After getting the access_token in the fifth step, send a request to the user-info-uri address we configured to get the login user information. After getting the user information, go through the Spring Security login process again on client1. That's OK.

Those who need to learn the project can go to my github to clone
the GitHub source code address

Guess you like

Origin blog.csdn.net/huangxuanheng/article/details/119052844