This example is a part of the mall background project, mainly using oAuth2 password mode to complete the user name password authentication function. The main process is: Using Nacos as the registry, the operator’s service user-Mgr-service as the service provider is registered with Nacos and invoked by oAuth2 through Dubbo. Meanwhile, oAuth2 is also registered with Nacos as the Rest service provider. The user login /user/login service is provided. Gateway Gateway also registers with Nacos, provides a unified entrance, routes to the oAuth2 service, and completes user authentication.

The article mainly writes the realization steps, the specific code is attached too much, affects the reading. Github.com/toyranger/c…).

1. Spring Security oAuth2 password mode

1.1 Password Mode and Entitlement Code Mode

{placeholder}

2. OAuth2 implements authentication server

2.1 Creating an Authorization Server

Refer to previous articles: juejin.cn/post/684490…

ClientDetailsServiceConfigurer: through the configuration of data sources, configuration ClientDetailsService AuthorizationServerSecurityConfigurer: Used to configure security constraints for Token endpoints. AuthorizationServerEndpointsConfigurer: used to configure authorization (authorization) and token (token)

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

  @Autowired
  private BCryptPasswordEncoder passwordEncoder;

  @Bean
  @Primary
  @ConfigurationProperties(prefix = "spring.datasource")
  public DataSource dataSource() {
    return DataSourceBuilder.create().build();
  }

  @Bean
  public TokenStore tokenStore() {
    return new JdbcTokenStore(dataSource());
  }

  @Bean
  public ClientDetailsService jdbcClientDetailsService() {
    returnnew JdbcClientDetailsService(dataSource()); } /*** * used to support password mode */ @autoWired Private AuthenticationManager AuthenticationManager; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore()); } @ Override public void the configure (AuthorizationServerSecurityConfigurer security) throws the Exception {/ / allow the client to access / request/check_token check token security. CheckTokenAccess ("isAuthenticated()").allowFormAuthenticationForClients(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(jdbcClientDetailsService()); } / * * * * * / / / @ Override/memory mode/public void the configure (ClientDetailsServiceConfigurer clients) throws the Exception {/ / / / clients.inMemory() // .withClient("client")
//        .secret(passwordEncoder.encode("secret"))
//        .authorizedGrantTypes("password"."refresh_token")
//        .scopes("backend")
//        .resourceIds("backend-resources") // .accessTokenValiditySeconds(60 * 60 * 24) // .refreshTokenValiditySeconds(60 * 60 * 24 * 30); / /}}Copy the code

2.2 Creating an Authentication Server and resource Server

The task of the authentication server is to query users based on their user names and the permissions of the users. The task of the resource server is to configure the corresponding permissions for accessing resources (urls). Here they are in a Configuration

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

  @Bean
  public BCryptPasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

  @Bean
  public UserDetailsService userDetailsService() {
    returnnew UserDetailsServiceImpl(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()); } /*** * Supports the password mode * @return
   * @throws Exception
   */
  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/user/login");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling().and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .authorizeRequests()
        .antMatchers("/user/info").hasAnyAuthority("UserInfo")
        .antMatchers("/user/logout").hasAnyAuthority("UserLogout"); }}Copy the code

2.3 In the userDetailsService of user authentication, you need to invoke the service provided by user-Mgr-service through Dubbo

public class UserDetailsServiceImpl implements UserDetailsService {

  @Reference(version = "1.0.0")
  private UserMgrApi userMgrApi;

  @Reference(version = "1.0.0")
  private PermissionMgrApi permissionMgrApi;

  @Override
  public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

    User userByName = userMgrApi.selectOne(s);

    if (null == userByName) {
      return null;
    }

    List<GrantedAuthority> grantedAuthorities = Lists.newArrayList();
    List<Permission> permissions = permissionMgrApi.selectListByUserId(userByName.getId());
    permissions.forEach(permission -> {
      GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getEnname());
      grantedAuthorities.add(grantedAuthority);
    });

    returnnew org.springframework.security.core.userdetails.User(userByName.getUsername(), userByName.getPassword(), grantedAuthorities); }} User information is based on the RBAC authorization model. Query the user through username and query the corresponding permission through userId after finding the user. These are very simple DAO operations, which can be completed by using Mybatis (Plus).Copy the code

3. OAuth2 provides a REST interface, such as /user/login

OAuth2 Password requires username, password, grant_type, client_id, client_secret five parameters, but users only need to pass username and password, so the rest of the parameters need to log in to the service itself. The RestTemplate is used here to make a request to the oAuth2 service. After successful authentication, the Token is obtained.

@PostMapping("/user/login")
  public CommonsResponse login(@RequestBody LoginParam loginParam) {

    String tokenUrl = "http://localhost:8091/oauth/token";
    MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
    multiValueMap.add("username", loginParam.getUsername());
    multiValueMap.add("password", loginParam.getPassword());
    multiValueMap.add("grant_type", oauth2_grant_type);
    multiValueMap.add("client_id", oauth2_client_id);
    multiValueMap.add("client_secret", oauth2_client_secret);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

    HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<>(multiValueMap, headers);
    TokenEntity tokenEntity;
    try {
      tokenEntity = restTemplate.postForObject(tokenUrl, entity, TokenEntity.class);
    } catch (Exception e) {
      tokenEntity = null;
    }

    if (null == tokenEntity) {
      return new CommonsResponse(BaseStatusEnum.UNAUTHORIZED.getIndex(),
          BaseStatusEnum.UNAUTHORIZED.getMsg(), null);
    }
    return new CommonsResponse(BaseStatusEnum.SUCCESS.getIndex(), BaseStatusEnum.SUCCESS.getMsg(),
        tokenEntity);
  }
Copy the code

4. Add the Gateway Gateway

The gateway can limit the flow and fuse to provide a unified entrance for applications. Only basic functionality is used here.

cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      Set up a routing policy for the service name in conjunction with the service registration discovery component
      discovery:
        locator:
          enable: true

      routes:
        - id: BUSINESS-OAUTH2
          LoadBalanceClient, starting with lb:// followed by the service name registered with nacos
          uri: lb://business-security
          Predicate, or predicate
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
Copy the code

5. Run the example

5.1 Registering with Nacos:

5.2 Accessing the Gateway:

5.3 Testing the Token Permission

You can see that.antmatchers (“/user/info”).hasanyauthority (“UserInfo”) is configured in the resource service, that is, access to /user/info requires UserInfo permission, and at this point in the RBAC table, user” user” has this permission

If I remove the /user/logout permission for user (tb_roLE_permission), it will return no permission when user accesses the URL:

If the permission is changed, the token obtained before will be invalid and needs to be obtained again.