OAuth2 和 SpringSecurity完成授權認證中心(一) 搭建授權認證中心(有項目源碼)

項目gitee 地址

授權服務

授權服務(Authorization Server) 應包含對接入端以及登陸用戶的合法性進行驗證並且頒發token等功能,對令牌的請求端點由 spring mvc 控制器進行實現,下面是一個認證服務需要實現的 endpoints:
AuthorizationEndpoint 服務用於認證請求,默認的 URL : /oauth/authorize
TokenEndpoint 服務於訪問令牌的請求 默認URL: /oauth/token
資源服務(ResourceServer) 應包含對資源的保護功能,對非法請求進行攔截,對請求中 token 進行解析鑑權等,下面的過濾器用於實現 OAuth2資源服務
OAuth2AuthenticationProcessingFilter 用來對請求給出的身份令牌解析鑑權。

等下我們創建一個類似於下方結構的認證授權
在這裏插入圖片描述
認證流程如下:
1. 客戶端請求uaa授權服務
2. 認證通過後由uaa頒發令牌
3. 客戶端攜帶令牌token 請求資源服務
4. 資源服務校驗令牌的合法性,合法即返回資源信息

環境搭建

初始化數據庫SQL

/*
Navicat MySQL Data Transfer



Target Server Type    : MYSQL
Target Server Version : 80013
File Encoding         : 65001

Date: 2019-11-12 17:18:59
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for t_permission
-- ----------------------------
DROP TABLE IF EXISTS `t_permission`;
CREATE TABLE `t_permission` (
  `id` varchar(32) NOT NULL,
  `code` varchar(32) NOT NULL,
  `description` varchar(64) DEFAULT NULL,
  `url` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_permission
-- ----------------------------
INSERT INTO `t_permission` VALUES ('1', 'p1', '測試資源1', '/r/r1');
INSERT INTO `t_permission` VALUES ('2', 'p3', '測試資源2', '/r/r2');

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` varchar(32) NOT NULL,
  `role_name` varchar(255) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `status` char(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_role_name` (`role_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES ('1', '管理員', null, null, null, '');

-- ----------------------------
-- Table structure for t_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `t_role_permission`;
CREATE TABLE `t_role_permission` (
  `role_id` varchar(32) NOT NULL,
  `permission_id` varchar(32) NOT NULL,
  PRIMARY KEY (`role_id`,`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_role_permission
-- ----------------------------
INSERT INTO `t_role_permission` VALUES ('1', '1');
INSERT INTO `t_role_permission` VALUES ('1', '2');

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` bigint(20) NOT NULL,
  `username` varchar(64) NOT NULL,
  `password` varchar(255) NOT NULL,
  `fullname` varchar(255) NOT NULL,
  `moblie` varchar(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES ('1', 'zhangsan', '$2a$10$aFsOFzujtPCnUCUKcozsHux0rQ/3faAHGFSVb9Y.B1ntpmEhjRtru', '張三', '11011912011');

-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `user_id` varchar(32) NOT NULL,
  `role_id` varchar(32) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `creator` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES ('1', '1', null, null);

項目最終結構

在這裏插入圖片描述

創建maven 父級工程
pop.xml 文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.fllday.security</groupId>
    <artifactId>distributed-security</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>distributed-security-order</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.interceptor</groupId>
                <artifactId>javax.interceptor-api</artifactId>
                <version>1.2</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-jwt</artifactId>
                <version>1.0.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security.oauth.boot</groupId>
                <artifactId>spring-security-oauth2-autoconfigure</artifactId>
                <version>2.1.3.RELEASE</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <finalName>${project.name}</finalName>
        <resources>
            <resource>
                <directory>src/main/resource</directory>
                <filtering>true</filtering>
                <includes>
                    <include>**/*</include>
                </includes>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <configuration>
                    <encoding>utf-8</encoding>
                    <useDefaultDelimiters>true</useDefaultDelimiters>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

創建子項目 uaa 授權服務中心
pop.xml文件如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>cn.fllday.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>distributed-security-uaa</artifactId>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-javanica</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <!--<exclusions>-->
                <!--<exclusion>-->
                    <!--<groupId>org.springframework.security.oauth.boot</groupId>-->
                    <!--<artifactId>spring-security-oauth2-autoconfigure</artifactId>-->
                <!--</exclusion>-->
            <!--</exclusions>-->
        </dependency>

        <dependency>
            <groupId>javax.interceptor</groupId>
            <artifactId>javax.interceptor-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

引入的maven 依賴如下
uua 引入的maven 依賴
uua 模塊的項目結構如下
uua項目結構
application.properties

spring.application.name=uaa-service
server.port=53020
spring.main.allow-bean-definition-overriding=true

#logging.level.root = debug
#logging.level.org.springframework.web = info

spring.http.encoding.enabled=true
spring.http.encoding.charset=utf-8
spring.http.encoding.force=true

server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
server.tomcat.max-http-post-size=5MB
server.use-forward-headers=true
server.servlet.context-path=/uaa

spring.freemarker.enabled=true
spring.freemarker.suffix=.html
spring.freemarker.request-context-attribute=rc
spring.freemarker.content-type=text/html
spring.freemarker.charset=UTF-8
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

spring.datasource.url=jdbc:mysql://ip:port/user_db?useUnicode=true
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

management.endpoints.web.exposure.exclude=refresh,health,info,env

feign.hystrix.enabled=true
feign.compression.request.enabled=true
feign.compression.request.mime-types[0]=text/xml
feign.compression.request.mime-types[1]=application/xml
feign.compression.request.mime-types[2]=application/json
feign.compression.request.min-request-size=2048
feign.compression.response.enabled=true

實體類根據SQL文件創建就可以了。一個一個對應起來就ok了。我就不寫了

UserDao.java

@Repository
public class UserDao {


    @Autowired
    JdbcTemplate jdbcTemplate;

    public UserDto getUserByUsername(String usernmae)
    {
        String sql = "select id,username,password,fullname from t_user where username = ?";
        List<UserDto> result = jdbcTemplate.query(sql, new Object[]{usernmae}, new BeanPropertyRowMapper<>(UserDto.class));
        if (result== null && result.size() <= 0)return null;
        return result.get(0);
    }
    // 根據用戶id 查詢用戶權限
    public List<String> findPermissionByUserId(String userId)
    {
        String sql = "SELECT * FROM t_permission WHERE id IN (" +
                " SELECT permission_id FROM t_role_permission WHERE role_id IN (" +
                " SELECT role_id FROM t_user_role WHERE user_id = ? " +
                ")" +
                ")";
        List<PermissionDto> result = jdbcTemplate.query(sql, new Object[]{userId}, new BeanPropertyRowMapper<>(PermissionDto.class));
        List<String> permissions = new ArrayList<String>();
        result.forEach( p->permissions.add(p.getCode()) );
        return permissions;
    }
}

SpringDataUserDetailsService這個類需要實現 UserDetailsService

@Service
public class SpringDataUserDetailsService implements UserDetailsService {

    @Autowired
    private UserDao userDao;
    
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        UserDto bean = userDao.getUserByUsername(s);
        if (bean == null)
            return null;
        List<String> permissions = userDao.findPermissionByUserId(bean.getId());
        String[] permissionArray = new String[permissions.size()];
        permissions.toArray(permissionArray);
        UserDetails userDetails = User.withUsername(bean.getUsername()).password(bean.getPassword()).authorities(permissionArray).build();
        return userDetails;
    }
}

下面就是配置類了
TokenConfig token存儲策略

@Configuration
public class TokenConfig {
    // 令牌存儲策略
    @Bean
    public TokenStore tokenStore()
    {
        // 內存方式,生成普通令牌
        return new InMemoryTokenStore();
    }
}

授權服務器配置類
需要使用 @EnableAuthorizationServer 註解並集成 AuthorizationServerConfigurerAdapter 來配置 OAuth2.0 授權服務器
AuthorizationServerConfigurerAdapter 要求配置以下幾個類,這幾個類是由Spring創建的獨立配置對象,他們會被Spring傳入 AuthorizationServerConfigurer 進行配置

ClientDetailsServiceConfigurer: 用來配置客戶端詳情服務(ClientDetailService),客戶端詳情信息在這裏進行初始化,你可以把客戶端詳情信息寫死在這裏或者通過數據庫來配置,我們這邊寫死在這裏就好了
AuthorizationServerEndpointsConfigurer: 用來配置令牌(token)的訪問斷電和令牌服務(token services
AuthorizationServerSecurityConfigurer: 用來配置令牌端點的安全約束

配置客戶端詳細信息

ClientDetailsServiceConfigurer 負責查找 ClientDetails,而ClientDetails 有幾個重要屬性如表:

屬性名 作用
clientId 必須的 用來標識客戶端的id
secret 需要值得信任的客戶端 , 客戶端安全碼,如果有的話
scope 用來限制客戶端的訪問範圍,如果爲空(默認),那麼客戶端擁有全部的訪問範圍
authorizedGrantTypes 由此客戶端可以使用的授權類型,默認爲空
authorities 此客戶端可以使用的權限(基於SpringSecurity authorities)

客戶端詳情(client details)能夠在應用程序運行的時候進行更新,可以通過訪問底層的存儲服務,例如將客戶端向高清存儲在一個關係數據庫的表中,你就可以使用jdbcClientDetailsService 或者通過自己實現 ClientDetailsService 來進行管理

管理令牌

AuthorizatonServerTokenService 接口定義了一些操作使你可以進行一些必要的管理,令牌可以被用來加載身份信息。

  1. InMemoryTokenStore: 這個版本是被默認採用的。 我們的授權認證也是基於這個
  2. JdbcTokenStore: 這是一個基於JDBC的版本,看名字也知道,使用的時候需要 引入依賴spring-jdbc
  3. JwtTokenStore: 這個版本全程是 JSON Web Token (Jwt) 他可以吧令牌相關的數據進行編碼,因此對於後臺來說,他不需要進行存儲,這是一個優勢,但是他也有一個缺點,那就是撤銷一個已經授權的令牌將會非常困難,所以它通常用來處理一個生命週期比較短的令牌以及撤銷刷新令牌(refresh_token)。另一個缺點就是這個令牌佔用的空間比較大,如果你加入了比較多的用戶憑證信息,Jwt不會保存任何數據。

我們這邊選擇內存方式。

配置授權類型(Grant Types)

AuthorizationServerEndpointsConfigurer 通過設定以下屬性支持的授權類型(GrantTypes)
authenticationManager: 認證管理器,當你選擇了資源所有者密碼(password)授權類型的時候,請設置這個屬性注入一個AuthenticationManager對象
userDetailsService 如果你設置這個屬性的話,說明你有一個自己的UserDetailsService接口的實現。
authorizationCodeServices: 這個屬性是用來設置授權碼服務的,主要用於authorization_code授權碼類型模式
implicatGrantService: 設置隱式授權模式,用來管理隱式授權模式的狀態
toeknGranter 授權將會交給你自己來完全掌控

默認的URL鏈接如下
 /**
     * /oauth/authorize     授權端點
     * /oauth/token         令牌斷點
     * /oauth/confirm-access用戶確認授權提交端點
     * /auth/error          授權服務錯誤信息斷電
     * /auth/check_token    用戶資源服務訪問的令牌解析斷電
     * /oauth/token_key     提供公有密鑰的端點,如果你使用jwt令牌的話
     */

需要注意的是這個授權端點URL應該被SpringSecurity保護起來只提供給授權用戶訪問

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private AuthorizationCodeServices authorizationCodeServices;


    @Bean
    public AuthorizationCodeServices authorizationCodeServices()
    {
        return new InMemoryAuthorizationCodeServices();
    }
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private ClientDetailsService clientDetailsService;


    // 配置客戶端詳細信息
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // 使用內存存儲
            .withClient("c1") // client_id
            // 客戶端密鑰
            .secret(new BCryptPasswordEncoder().encode("secret"))
            // 資源列表
            .resourceIds("res1")
            // 授權方式
            .authorizedGrantTypes("authorization_code","password","client_creentials","implicit","refresh_token")
            // 允許授權的範圍
            .scopes("all")
            //
            .autoApprove(false)  // false 跳轉到授權頁面
            // 加上驗證回調地址
            .redirectUris("http://www.baidu.com");

    }



    // 令牌管理服務
    public AuthorizationServerTokenServices tokenServices()
    {
        DefaultTokenServices service = new DefaultTokenServices();
        service.setClientDetailsService(clientDetailsService);  // 客戶端信息服務
        service.setSupportRefreshToken(true);   // 是否產生刷新令牌
        service.setTokenStore(tokenStore);  // 設置令牌存儲策略
        service.setAccessTokenValiditySeconds(7200);// 令牌默認有效期 2 小時
        service.setRefreshTokenValiditySeconds(259200);
        return service;
    }

    /**
     * /oauth/authorize     授權端點
     * /oauth/token         令牌斷點
     * /oauth/confirm-access用戶確認授權提交端點
     * /auth/error          授權服務錯誤信息斷電
     * /auth/check_token    用戶資源服務訪問的令牌解析斷電
     * /oauth/token_key     提供公有密鑰的端點,如果你使用jwt令牌的話
     */

    /**
     *  令怕i訪問端點
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // 密碼管理模式
                .authenticationManager(authenticationManager)
                // 授權碼模式
                .authorizationCodeServices(authorizationCodeServices)
                .tokenServices(tokenServices()) // 令牌管理服務
                .allowedTokenEndpointRequestMethods(HttpMethod.POST); // 允許post提交
    }


    /**
     *  配置令牌端點(Token Endpoint)的安全約束
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")         // /oauth/token_key  公開
                .checkTokenAccess("permitAll()")       // /auth/check_token  檢測令牌
                .allowFormAuthenticationForClients();  // 允許通過表單認證,申請令牌

        super.configure(security);
    }
}

授權服務配置總結
授權服務分成三大塊,可以關聯記憶。
既然要完成,它首先得知道客戶端從哪裏讀取,因此要進行客戶端詳情配置,既然要頒發token,那麼必須要定義 token的相關 endpoint , 以及 token 如何存取,以及客戶端支持那些類型的 token。
既然暴露了一些 endpoint, 那對這些 endpoint 可以定義一些安全上的約束等。

Web 安全配置

配置 WebSecirutyConfig.java , 繼承WebSecurityConfigurerAdapter

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //認證管理器
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    // 安全攔截機制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/r/r1").hasAnyAuthority("p1")
                .antMatchers("/login*").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
        ;

    }
}

配置啓動類
UaaServer.java

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableFeignClients(basePackages = {"cn.fllday.security.distruibuted.uaa"})
public class UaaServer {
    public static void main(String[] args) {
        SpringApplication.run(UaaServer.class);
    }
}

到這裏我們的授權認證中心就已經完成了。接下來配置一個客戶端。

創建一個order 的模塊
pom.xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.fllday.security</groupId>
    <artifactId>distributed-security-order</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <artifactId>distributed-security</artifactId>
        <groupId>cn.fllday.security</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.interceptor</groupId>
            <artifactId>javax.interceptor-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

引入的依賴如下
引入的maven依賴
application.properties 文件

spring.application.name=order-service
server.port=53022
spring.main.allow-bean-definition-overriding=true

logging.level.root=debug
logging.level.org.springframework.web=info
spring.http.encoding.enabled=true
spring.http.encoding.charset=utf-8
spring.http.encoding.force=true
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.protocol-header=x-forwarded-proto
server.use-forward-headers=true
server.servlet.context-path=/order


spring.freemarker.enabled=true
spring.freemarker.suffix=.html
spring.freemarker.request-context-attribute=rc
spring.freemarker.content-type=text/html
spring.freemarker.charset=utf-8
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
資源服務器配置

@EnableResourceServer 註解到一個@Configuration 配置類上,並且必須使用ResourceServerConfigurer 這個配置對象來進行配置。 下面是一些可以配置的屬性

  • tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌服務
  • tokenStore:TokenStore類的實力,制定令牌如何訪問,與tokenServices 配置可選
  • resourceId : 這個資源服務器的id,這個屬性是可選的,但是推薦設置並在授權服務中進行驗證
  • 其他的拓展例tokenExtractor 令牌提取器用來提取請求中的令牌

HttpSecurity 配置這個與SpringSecurity 類似

  • 請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是保護資源服務器全部路徑。
  • 通過http.authorizeRequests() 來設置受保護資源的訪問規則
  • 其他的自定義權限保護規則通過 httpSecurity 來進行配置

@EnableResourceServer 註解自動增加了一個類型爲Oauth2AuthenticationProcessingFilter 的過濾器鏈

ResourceServerConfig.java

package cn.fllday.security.distributed.order.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    public static final String RESOURCE_ID = "res1";

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**").access("#oauth2.hasScope('all')")
                .and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        super.configure(http);
    }
    // 資源服務令牌解析服務
    /**
     * 使用遠程服務請求授權服務器校驗的token,必須制定校驗token 的url, client_id,client_secret
     * @return
     */
    @Bean
    public ResourceServerTokenServices tokenServices()
    {
        RemoteTokenServices service = new RemoteTokenServices();
        service.setCheckTokenEndpointUrl("http://localhost:53020/uaa/oauth/check_token");
        service.setClientId("c1");
        service.setClientSecret("secret");
        return service;
    }
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId(RESOURCE_ID) // 資源id
                .tokenServices(tokenServices()) // 驗證令牌的服務
                .stateless(true);
    }
}
添加安全訪問控制

WebSecurityConfig.java

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/r/**").authenticated() // 表示 /r/的路徑都會受到保護
                .anyRequest().permitAll();
    }
}
創建資源接口

OrderController.java

@RestController
@RequestMapping(value = "/r")
public class OrderController {

    @GetMapping(value = "/r1")
    @PreAuthorize("hasAnyAuthority('p1')") // 擁有p1權限方可發個文
    public String r1()
    {
        return "訪問資源1";
    }

    @GetMapping(value = "/r2")
    @PreAuthorize("hasAnyAuthority('p2')") // 擁有p2權限方可發個文
    public String r2()
    {
        return "訪問資源2";
    }
}

配置啓動類
Order.java

@SpringBootApplication
@EnableDiscoveryClient
public class OrderServer {
    public static void main(String[] args) {
        SpringApplication.run(OrderServer.class,args);
    }
}

順序啓動 uaa 和 order

測試

這裏使用的工具是postman
我們先嚐試訪問獲取token的接口
在這裏插入圖片描述
這裏的參數都是哪裏來的呢?
在這裏插入圖片描述
發現原來在我們的AuthorizationServer類中configure方法中ClientDetailsServiceConfigurer 配置過了
那麼code 哪裏來的呢?
我們使用瀏覽器訪問http://localhost:53020/uaa/login有一個登陸頁面。我們使用數據庫中添加的用戶名和密碼,如果你用的我的sql文件的話,用戶名 zhangsan, 密碼 123。登陸成功後訪問http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com, 出現一個頁面爲
在這裏插入圖片描述
點擊Authorize
你的頁面就跳轉到百度了。 但是瀏覽器後面會跟上一串字符串
在這裏插入圖片描述
訪問token接口的code 參數的值就是這麼來的

訪問獲取token的接口
在這裏插入圖片描述
訪問資源
在這裏插入圖片描述
不懈怠token 就會報錯,總之就是不同意你訪問這個資源
在這裏插入圖片描述
注意看我圖裏綠色圈圈起來的地方。token 需要加上 Bearer 有個空格哦!!

訪問沒有資源的接口
在這裏插入圖片描述
下面我們去看看那個校驗token的接口做了什麼
在這裏插入圖片描述
上面的不用解釋了吧!
至於其他的模式。等下次在挨個解釋吧。 下一篇文章會使用jwt,以及通過 springcloud 的網關來控制。

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