基於oauth2簡單實現SSO

  • 在實現SSO之前,先要說一下**@EnableResourceServer@EnableOAuth2Sso**
  1. 一般網關處我們使用EnableResourceServer時,可以支持任意的oauth2授權模型,網關聲明稱資源服務器可以配置ResourceServerConfigurerAdapter,我們可以配置放行的路徑等等內容。
  2. 如果網關處使用EnableOAuth2Sso,在所有請求轉發之前就首先要到認證服務器上去校驗信息,使用授權碼模式,clientid與clientsecret來確認進行oauth2的登陸,跳轉登陸頁面在進行登陸。

下面我們來簡單搭建一個SSO的小項目

  • 搭建認證服務器

    我們將客戶端信息存儲在mysql中,token使用jwt。

    依賴包只需要oauth2,我cloud用的版本是Greenwich.SR2

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    
    1. 配置AuthorizationServerConfigurerAdapter

      package cn.hs.sys.config;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.authentication.AuthenticationManager;
      import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
      import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
      import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
      import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
      import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
      import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
      import org.springframework.security.oauth2.provider.token.TokenStore;
      import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
      import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
      
      import javax.sql.DataSource;
      
      /**
       * @description:
       * @Author: huangsan
       * @Date: 2020/5/8 4:46 下午
       */
      @Configuration
      @EnableAuthorizationServer
      public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {
      
          @Autowired
          private AuthenticationManager authenticationManager;
          @Autowired
          private DataSource dataSource;
          @Autowired
          private BCryptPasswordEncoder passwordEncoder;
      
          @Override
          public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
              endpoints
                      .tokenStore(tokenStore())
                      .tokenEnhancer(jwtAccessTokenConverter())
                      .authenticationManager(authenticationManager);
          }
      
          @Bean
          public TokenStore tokenStore() {
              return new JwtTokenStore(jwtAccessTokenConverter());
          }
      
          @Bean
          public JwtAccessTokenConverter jwtAccessTokenConverter() {
              JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
              converter.setSigningKey("123456");
              return converter;
          }
      
          @Override
          public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
              System.out.println("------"+passwordEncoder.encode("123456"));
              clients.jdbc(dataSource);
          }
      
          @Override
          public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
              security
                      .tokenKeyAccess("isAuthenticated()")
                      .checkTokenAccess("isAuthenticated()");
          }
      }
      
    2. 配置WebSecurityConfigurerAdapter

      package cn.hs.sys.config;
      
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.authentication.AuthenticationManager;
      import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
      import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
      import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
      import org.springframework.security.core.userdetails.UserDetailsService;
      import org.springframework.security.crypto.password.PasswordEncoder;
      
      /**
       * @description:
       * @Author: huangsan
       * @Date: 2020/5/8 4:47 下午
       */
      @Configuration
      @EnableWebSecurity
      public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter {
      
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Autowired
          private PasswordEncoder passwordEncoder;
      
          @Override
          protected void configure(AuthenticationManagerBuilder auth) throws Exception {
              auth.userDetailsService(userDetailsService)
                      .passwordEncoder(passwordEncoder);
          }
        
          @Bean
          @Override
          public AuthenticationManager authenticationManagerBean() throws Exception {
              return super.authenticationManagerBean();
          }
      }
      
    3. 配置UserDetailsService(這裏自己實現邏輯返回UserDetails即可)

      package cn.hs.sys.config;
      
      import cn.hs.sys.Constant;
      import cn.hs.sys.dao.SysUserEntity;
      import cn.hs.sys.dao.UserPermissionRepository;
      import org.apache.commons.lang.StringUtils;
      import org.springframework.security.core.GrantedAuthority;
      import org.springframework.security.core.authority.SimpleGrantedAuthority;
      import org.springframework.security.core.userdetails.User;
      import org.springframework.security.core.userdetails.UserDetails;
      import org.springframework.security.core.userdetails.UserDetailsService;
      import org.springframework.security.core.userdetails.UsernameNotFoundException;
      import org.springframework.stereotype.Service;
      
      import javax.annotation.Resource;
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @description:
       * @Author: huangsan
       * @Date: 2020/5/8 4:53 下午
       */
      @Service
      public class UserDetailsServiceImpl implements UserDetailsService {
      
          @Resource
          private UserPermissionRepository userPermissionRepository;
      
          @Override
          public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
              if (StringUtils.isBlank(username)) {
                  throw new UsernameNotFoundException("賬號錯誤");
              }
              SysUserEntity sysUserEntity = userPermissionRepository.findUserIdByName(username);
              if (sysUserEntity == null) {
                  throw new UsernameNotFoundException("賬號異常");
              }
              if (Constant.USER_OFF.equals(sysUserEntity.getUserStatus())) {
                  throw new UsernameNotFoundException("賬號狀態異常");
              }
              if (sysUserEntity.getUserId() != null) {
                  List<String> permissionStrs = userPermissionRepository.listPermissionById(sysUserEntity.getUserId());
      
                  List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
                  permissionStrs.forEach(permissionStr -> {
                      if (StringUtils.isNotBlank(permissionStr)) {
                          GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionStr);
                          grantedAuthorities.add(grantedAuthority);
                      }
                  });
                  //密碼加密方式爲:passwordEncoder.encode()
                  return new User(username, sysUserEntity.getUserPassword(), grantedAuthorities);
              } else {
                  throw new UsernameNotFoundException("賬號異常");
              }
          }
      }
      
    4. 配置文件如下(沒什麼東西,主要就是mysql的內容,eureka自行去掉吧)

      server:
        port: 52188
      spring:
        application:
          name: hs-auth
        datasource:
          url: jdbc:mysql://localhost:3306/hs_cloud?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true
          username: root
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
      eureka:
        client:
          service-url:
            defaultZone: http://127.0.0.1:52199/eureka
      

認證服務器的內容基本都是一樣,沒什麼特別的地方我就直接貼代碼了,對於客戶端來說有一些需要注意的地方。

  • 配置客戶端

    注意客戶端不能使用EnableResourceServer註解

    1. 引入相關依賴

      <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>
      
    2. 在啓動類上增加@EnableOAuth2Sso註解

    3. 創建測試web測試類,返回數據

    4. 配置yml文件

      server:
        port: 52109
        servlet:
          context-path: /sys #特別需要配置,如果是/會反覆跳轉登陸頁面
      security:
        oauth2:
          resource:
            jwt:
              key-uri: http://localhost:52188/oauth/token_key
          client:
            client-id: hs-system #客戶端id
            client-secret: 123456 #客戶端口令
            access-token-uri: http://localhost:52188/oauth/token #獲取令牌路徑
            user-authorization-uri: http://localhost:52188/oauth/authorize #獲取授權路徑
      
  • 相同的方式配置另一個客戶端

    和第一個客戶端一樣,注意更改path

  • 配置完客戶端需要配置登陸跳轉路徑和自動授權(下面是sql插入語句自行修改)

    INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-gateway', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52100/zuul/login', NULL, 3600, 360000, NULL, 'read'); INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-modules-back', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52110/back/login', NULL, 3600, 360000, NULL, 'read');

    主要注意web_server_redirect_uri與autoapprove,配置autoapprove可以跳過授權的步驟,直接授權

  • 成果演示(文字描述是:倆個客戶端訪問的時候都會去授權服務器,如果沒有登陸會跳轉登陸頁,當一個客戶端登錄後,可直接訪問,不需登陸。視頻傳上來好麻煩,就不傳了,手動狗頭。)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章