Need to say first, Songko has recently written a series of tutorials, there are some repetitive things to write and write is not interesting, so each article is assumed that you have already understood the previous content, so the following any do not understand the place, it is recommended to read the relevant series:

Spring Security series:

  1. Dig a big hole and Spring Security will do it!
  2. How to decrypt the password
  3. A step-by-step guide to customizing form logins in Spring Security
  4. Spring Security does front and back separation, so don’t do page jumps! All JSON interactions
  5. Authorization in Spring Security used to be so simple
  6. How does Spring Security store user data into the database?
  7. Spring Security+Spring Data Jpa, Security management is only easier!

OAuth2 series:

  1. Do micro services around OAuth2, songge also come and you pull a pull
  2. This case to write out, but also afraid to tell the interviewer do not understand OAuth2 login process?
  3. OAuth2, coach, I want to learn the whole thing!
  4. Can OAuth2 tokens still be stored in Redis? More play more slip!
  5. Want OAuth2 and JWT to have fun together? Watch Songo’s performance
  6. I would like to share with you some ideas of security management in microservices architecture

All right, let’s start today’s text.

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

Distributed system is composed 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 logged in, do not have to log in again. OAuth2+JWT = JWT = JWT = JWT = JWT = JWT = JWT = JWT = JWT = JWT Watch Songo’s performance.

Of course, as you all know, stateless login has its drawbacks.

Spring Boot+OAuth2 single sign-on, using @enableoAuth2SSO annotation to quickly implement single sign-on function.

Songo still recommends that you read the previous articles in this series to get a better understanding of this article.

1. Project creation

In the previous case, Songgo always created the authorization server and the resource server separately. In this case, to save trouble, I will build the authorization server and the resource server together (but I believe you read the previous article, should also be able to separate the two servers).

So, today we need three services altogether:

project port describe
auth-server 1111 Authorization server + resource server
client1 1112 Subsystem 1
client2 1113 Subsystem 2

Auth-server plays the role of authorization server + resource server, while Client1 and Client2 play the role of subsystem respectively. In the future, after client1 logs in successfully, we can also access Client2, so that we can see the effect of single sign-on.

Let’s create a Maven project named oAuth2-SSO as the parent project.

2. Unified certification center

Next, let’s build a unified certification center.

Create a module named auth-server with the following dependencies:

After the project is successfully created, this module will play the role of authorization server + resource server, so we first add @enableresourceserver comment on the project startup class, indicating that this is a resource server:

@SpringBootApplication
@EnableResourceServer
public class AuthServerApplication {

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

Next, let’s configure the authorization server. Since the resource server and authorization server are merged together, the authorization server configuration is much easier:

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("javaboy")
                .secret(passwordEncoder.encode("123"))
                .autoApprove(true)
                .redirectUris("http://localhost:1112/login"."http://localhost:1113/login")
                .scopes("user")
                .accessTokenValiditySeconds(7200)
                .authorizedGrantTypes("authorization_code"); }}Copy the code

Here we just need a simple configuration of the client information, the configuration here is very simple, the previous article also said, if you do not understand, you can refer to the previous article in this series: this case to write out, but also afraid to pull with the interviewer do not understand OAuth2 login process? .

Of course, for the sake of simplicity, the client information configuration is based on memory, if you want to store the client information in the database, it is also possible, refer to: OAuth2 token can be stored in Redis? More play more slip!

Now let’s configure Spring Security:

@Configuration
@Order(1)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder(a) {
        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("sang")
                .password(passwordEncoder().encode("123"))
                .roles("admin"); }}Copy the code

If you don’t understand the configuration of Spring Security, take a look at the Spring Security series that Songo is currently serializing.

Let me outline it here:

  1. First provide an instance of BCryptPasswordEncoder, used to do password encryption and decryption.
  2. Since I customized the login page, I square these static resources in WebSecurity.
  3. In HttpSecurity, we allow authentication related endpoints and configure the login page and login interface.
  4. Offer a memory-based AuthenticationManagerBuilder users (friends can adjust itself according to the Spring Security series 7 article is loaded from the database).
  5. Another key point is that because the resource server and authorization server are together, we need an @Order annotation to increase the priority of the Spring Security configuration.

SecurityConfig and AuthServerConfig are both required by the authorization server. Next, we need to provide an interface to expose user information (if authorization server and resource server are separated, This interface will be provided by the resource server) :

@RestController
public class UserController {
    @GetMapping("/user")
    public Principal getCurrentUser(Principal principal) {
        returnprincipal; }}Copy the code

Finally, we configure the project port in application.properties:

server.port=1111
Copy the code

In addition, Songo himself prepared a login page in advance, which reads as follows:

Copy the HTML, CSS, js, etc. related to the login page to the resources/static directory:

The page is simple, just a login form, and I’ve listed the core:

<form action="/login" method="post">
    <div class="input">
        <label for="name">The user name</label>
        <input type="text" name="username" id="name">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="pass">password</label>
        <input type="password" name="password" id="pass">
        <span class="spin"></span>
    </div>
    <div class="button login">
        <button type="submit">
            <span>The login</span>
            <i class="fa fa-check"></i>
        </button>
    </div>
</form>
Copy the code

The action submission address is correct.

You can download the source code at the end of the article.

After this, our unified authentication login platform is OK.

3. Create a client

To create a client project, create a Spring Boot project named Client1 and add the following dependencies:

After the project is 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(); }}Copy the code

This configuration is simple, meaning that all interfaces in client1 need to be authenticated to access, and an @enableoAuth2SSO annotation is added to enable single sign-on.

Let’s provide another test interface in client1:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello(a) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        returnauthentication.getName() + Arrays.toString(authentication.getAuthorities().toArray()); }}Copy the code

This test interface returns the name and role information of the currently logged in user.

Next we need to configure the information about oAuth2 in client1’s application.properties:

security.oauth2.client.client-secret=123
security.oauth2.client.client-id=javaboy
security.oauth2.client.user-authorization-uri=http://localhost:1111/oauth/authorize
security.oauth2.client.access-token-uri=http://localhost:1111/oauth/token
security.oauth2.resource.user-info-uri=http://localhost:1111/user

server.port=1112

server.servlet.session.cookie.name=s1
Copy the code

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

  1. Client-secret indicates the client password.
  2. Client-id indicates the ID of a client.
  3. User-authorization-uri is the endpoint for user authorization.
  4. Access-token-uri is the endpoint that obtains the token.
  5. User-info-uri is the interface to get user information (from the resource server).
  6. Finally, configure the port and give the cookie a name.

With this done, our client1 configuration is complete.

Client2 = client1; client2 = client1; client2 = client1;

4. Test

Next, we start auth-server, Client1 and Client2 respectively. First, we try to access the Hello interface in client1, which will automatically redirect to the unified authentication center:

Then enter your user name and password to log in.

After a successful login, the hello interface of Client1 is automatically redirected as follows:

Client2 = client2; client2 = client2; client2 = client2

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

5. Process analysis

In the end, I’ll give you a rundown of the above code:

  1. First we try to access client1’s/Hello interface, which requires login, so our request is blocked and redirected to client1’s /login interface, which tells us to login.

  1. When we access client1’s login interface, due to the @enableoAuth2SSO annotation, this operation is again blocked and the sso interceptor automatically issues a request for an authorization code based on our configuration in application.properties:

  1. The request sent in the second step is a request for the auth-server service, this request of course will not avoid the first login, so again redirected to the auth-server login page, that is, you see the unified authentication center.
  2. We complete the login function in the Unified Attention center, after the login, we will continue to execute the request of the second step, at this time we can successfully obtain the authorization code.

  1. After obtaining the authorization code, it will be redirected to the login page of our client1. However, in fact, our client1 does not have a login page, so this operation will still be intercepted. At this time, the intercepted address contains the authorization code. OAuth2ClientAuthenticationProcessingFilter class to auth – the server request, you can get the access_token (reference: This case to write out, but also afraid to tell the interviewer do not understand OAuth2 login process? .
  2. After obtaining the access_token in step 5, we will send a request to the user-info-URI address we configured to obtain the login user information. After obtaining the user information, we will go through the Spring Security login process on Client1 and we are OK.

SpringBoot +OAuth2 single sign-on: SpringBoot +OAuth2 single sign-on: OAuth2

If you think it’s helpful, click on a zongo.