springboot和springsecurity整合OAuth2


1. OAuth2.0介紹

OAuth(開放授權)是一個開放標准,允許用戶授權第三方應用訪問他們存儲在另外的服務提供者上的信息,而不需要將用戶名和密碼提供給第三方應用或分享他們數據的所有內容。OAuth2.0OAuth協議的延續版本,但不向后兼容OAuth 1.0即完全廢止了OAuth1.0。很多大公司如GoogleYahooMicrosoft等都提供了OAUTH認證服務,這些都足以說明OAUTH標准逐漸成為開放資源授權的標准。
Oauth協議目前發展到2.0版本,1.0版本過於復雜,2.0版本已得到廣泛應用。

下邊分析一個 Oauth2認證的例子,通過例子去理解OAuth2.0協議的認證流程,本例子是黑馬程序員網站使用微信認證的過程,這個過程的簡要描述如下:

用戶借助微信認證登錄黑馬程序員網站,用戶就不用單獨在黑馬程序員注冊用戶,怎么樣算認證成功嗎?黑馬程序
員網站需要成功從微信獲取用戶的身份信息則認為用戶認證成功,那如何從微信獲取用戶的身份信息?用戶信息的
擁有者是用戶本人,微信需要經過用戶的同意方可為黑馬程序員網站生成令牌,黑馬程序員網站拿此令牌方可從微
信獲取用戶的信息。

  • 1、 客戶端請求第三方授權
    用戶進入黑馬程序的登錄頁面,點擊微信的圖標以微信賬號登錄系統,用戶是自己在微信里信息的資源擁有者。
    點擊“微信”出現一個二維碼,此時用戶掃描二維碼,開始給黑馬程序員授權。
  • 2、 資源擁有者同意給客戶端授權
    資源擁有者掃描二維碼表示資源擁有者同意給客戶端授權,微信會對資源擁有者的身份進行驗證, 驗證通過后,微信會詢問用戶是否給授權黑馬程序員訪問自己的微信數據,用戶點擊“確認登錄”表示同意授權,微信認證服務器會頒發一個授權碼,並重定向到黑馬程序員的網站。
  • 3、 客戶端獲取到授權碼,請求認證服務器申請令牌
    此過程用戶看不到,客戶端應用程序請求認證服務器,請求攜帶授權碼。
  • 4、認證服務器向客戶端響應令牌
    微信認證服務器驗證了客戶端請求的授權碼,如果合法則給客戶端頒發令牌,令牌是客戶端訪問資源的通行證。
    此交互過程用戶看不到,當客戶端拿到令牌后,用戶在黑馬程序員看到已經登錄成功。
  • 5、客戶端請求資源服務器的資源
    客戶端攜帶令牌訪問資源服務器的資源。
    黑馬程序員網站攜帶令牌請求訪問微信服務器獲取用戶的基本信息。
  • 6、資源服務器返回受保護資源
    資源服務器校驗令牌的合法性,如果合法則向用戶響應資源信息內容。

以上認證授權詳細的執行流程如下:

OAuth2.0認證流程:

OAauth2.0包括以下角色:
1、客戶端
本身不存儲資源,需要通過資源擁有者的授權去請求資源服務器的資源,比如:Android客戶端、Web客戶端(瀏覽器端)、微信客戶端等。
2、資源擁有者
通常為用戶,也可以是應用程序,即該資源的擁有者。
3、授權服務器(也稱認證服務器)

用於服務提供商對資源擁有的身份進行認證、對訪問資源進行授權,認證成功后會給客戶端發放令牌
access_token),作為客戶端訪問資源服務器的憑據。本例為微信的認證服務器。
4、資源服務器
存儲資源的服務器,本例子為微信存儲的用戶信息。
現在還有一個問題,服務提供商能允許隨便一個客戶端就接入到它的授權服務器嗎?答案是否定的,服務提供商會
給准入的接入方一個身份,用於接入時的憑據:
client_id:客戶端標識

client_secret:客戶端秘鑰
因此,准確來說,授權服務器對兩種OAuth2.0中的兩個角色進行認證授權,分別是資源擁有者、客戶端。


2. OAuth2.0 中四種授權方式

1. 授權碼模式( authorization code)

流程

說明:【A服務客戶端】需要用到【B服務資源服務】中的資源

第一步:【A服務客戶端】將用戶自動導航到【B服務認證服務】,這一步用戶需要提供一個回調地址,以備【B服務認證服務】返回授權碼使用。
第二步:用戶點擊授權按鈕表示讓【A服務客戶端】使用【B服務資源服務】,這一步需要用戶登錄B服務,也就是說用戶要事先具有B服務的使用權限。
第三步:【B服務認證服務】生成授權碼,授權碼將通過第一步提供的回調地址,返回給【A服務客戶端】。

注意這個授權碼並非通行【B服務資源服務】的通行憑證。

第四步:【A服務認證服務】攜帶上一步得到的授權碼向【B服務認證服務】發送請求,獲取通行憑證token
第五步:【B服務認證服務】給【A服務認證服務】返回令牌token和更新令牌refresh token

使用場景

授權碼模式是OAuth2中最安全最完善的一種模式,應用場景最廣泛,可以實現服務之間的調用,常見的微信,QQ等第三方登錄也可采用這種方式實現。


2. 簡化模式(implicit)

流程

說明:簡化模式中沒有【A服務認證服務】這一部分,全部有【A服務客戶端】與B服務交互,整個過程不再有授權碼,token直接暴露在瀏覽器。

第一步:【A服務客戶端】將用戶自動導航到【B服務認證服務】,這一步用戶需要提供一個回調地址,以備【B服務認證服務】返回token使用,還會攜帶一個【A服務客戶端】的狀態標識state
第二步:用戶點擊授權按鈕表示讓【A服務客戶端】使用【B服務資源服務】,這一步需要用戶登錄B服務,也就是說用戶要事先具有B服務的使用權限。

第三步:【 B服務認證服務】生成通行令牌tokentoken將通過第一步提供的回調地址,返回給【A服務客戶端】。

使用場景

適用於A服務沒有服務器的情況。比如:純手機小程序,JavaScript語言實現的網頁插件等。


3. 密碼模式(resource owner password credentials)

流程

第一步:直接告訴【A服務客戶端】自己的【B服務認證服務】的用戶名和密碼
第二步:【A服務客戶端】攜帶【B服務認證服務】的用戶名和密碼向【B服務認證服務】發起請求獲取
token
第三步:【B服務認證服務】給【A服務客戶端】頒發token

使用場景

此種模式雖然簡單,但是用戶將B服務的用戶名和密碼暴露給了A服務,需要兩個服務信任度非常高才能使
用。


4. 客戶端模式(client credentials)

流程

說明:這種模式其實已經不太屬於OAuth2的范疇了。A服務完全脫離用戶,以自己的身份去向B服務索取token。換言之,用戶無需具備B服務的使用權也可以。完全是A服務與B服務內部的交互,與用戶無關了。

第一步:A服務向B服務索取token
第二步:B服務返回token給A服務。

使用場景

A服務本身需要B服務資源,與用戶無關。


3. OAuth2.0 sql語句

說明
既可以寫死在代碼中,也可以寫入到數據庫中,通常寫入到數據庫
建表語句
官方SQL地址:

create table oauth_client_details (
  client_id VARCHAR(256) PRIMARY KEY,
  resource_ids VARCHAR(256),
  client_secret VARCHAR(256),
  scope VARCHAR(256),
  authorized_grant_types VARCHAR(256),
  web_server_redirect_uri VARCHAR(256),
  authorities VARCHAR(256),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additional_information VARCHAR(4096),
  autoapprove VARCHAR(256)
);

create table oauth_client_token (
  token_id VARCHAR(256),
  token LONGVARBINARY,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256)
);

create table oauth_access_token (
  token_id VARCHAR(256),
  token LONGVARBINARY,
  authentication_id VARCHAR(256) PRIMARY KEY,
  user_name VARCHAR(256),
  client_id VARCHAR(256),
  authentication LONGVARBINARY,
  refresh_token VARCHAR(256)
);

create table oauth_refresh_token (
  token_id VARCHAR(256),
  token LONGVARBINARY,
  authentication LONGVARBINARY
);

create table oauth_code (
  code VARCHAR(256), authentication LONGVARBINARY
);

create table oauth_approvals (
	userId VARCHAR(256),
	clientId VARCHAR(256),
	scope VARCHAR(256),
	status VARCHAR(10),
	expiresAt TIMESTAMP,
	lastModifiedAt TIMESTAMP
);


-- customized oauth_client_details table
create table ClientDetails (
  appId VARCHAR(256) PRIMARY KEY,
  resourceIds VARCHAR(256),
  appSecret VARCHAR(256),
  scope VARCHAR(256),
  grantTypes VARCHAR(256),
  redirectUrl VARCHAR(256),
  authorities VARCHAR(256),
  access_token_validity INTEGER,
  refresh_token_validity INTEGER,
  additionalInformation VARCHAR(4096),
  autoApproveScopes VARCHAR(256)
);

4. demo案例

1. 創建父工程

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>oauth_resource</module>
        <module>oauth_server</module>
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>springboot_security_oauth</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_security_oauth</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.RELEASE</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

2. 創建資源提供方模塊

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">
    <parent>
        <artifactId>springboot_security_oauth</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>oauth_resource</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>

</project>

1. 配置application.yml

server:
  port: 9002
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///test
    username: root
    password: root
  main:
    allow-bean-definition-overriding: true
mybatis:
  type-aliases-package: com.example.domain
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    com.example: debug

2. 配置啟動類

@SpringBootApplication
@MapperScan("com.example.mapper")
public class OAuthResourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OAuthResourceApplication.class, args);
    }
}

3. 編寫一個資源路由

@RestController
@RequestMapping("/product")
public class ProductController {
    @GetMapping
    public String findAll() {
        return "查詢產品列表成功!";
    }
}

4. 創建用戶pojo和角色pojo

用戶pojo

public class SysUser implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Integer status;
    private List<SysRole> roles = new ArrayList<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public List<SysRole> getRoles() {
        return roles;
    }

    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }

    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}

角色pojo

public class SysRole implements GrantedAuthority {
    private Integer id;
    private String roleName;
    private String roleDesc;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    //標記此屬性不做json處理
    @JsonIgnore
    @Override
    public String getAuthority() {
        return roleName;
    }
}

5. 編寫資源管理配置類

@Configuration
@EnableResourceServer
public class OauthResourceConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private TokenStore tokenStore;

    /**
     * 指定token的持久化策略
     * 其下有   RedisTokenStore保存到redis中,
     * JdbcTokenStore保存到數據庫中,
     * InMemoryTokenStore保存到內存中等實現類,
     * 這里我們選擇保存在數據庫中
     *
     * @return
     */
    @Bean
    public TokenStore jdbcTokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("product_api")//指定當前資源的id,非常重要!必須寫!
                .tokenStore(tokenStore);//指定保存token的方式
    }

    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //指定不同請求方式訪問資源所需要的權限,一般查詢是read,其余是write。
                .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')")
                .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')")
                .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')")
                .and()
                .headers().addHeaderWriter((request, response) -> {
            response.addHeader("Access-Control-Allow-Origin", "*");//允許跨域
            if (request.getMethod().equals("OPTIONS")) {//如果是跨域的預檢請求,則原封不動向下傳達請求頭信息
                response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
                response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            }
        });
    }
}

3. 創建授權模塊

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">
    <parent>
        <artifactId>springboot_security_oauth</artifactId>
        <groupId>com.example</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>oauth_server</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>
</project>

1. 配置application.yml

server:
  port: 9001
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql:///test
    username: root
    password: root
  main:
    allow-bean-definition-overriding: true # 這個表示允許我們覆蓋OAuth2放在容器中的bean對象,一定要配置
mybatis:
  type-aliases-package: com.example.domain
  configuration:
    map-underscore-to-camel-case: true
logging:
  level:
    com.example: debug

2. 配置啟動類

@SpringBootApplication
@MapperScan("com.example.mapper")
public class OauthServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(OauthServerApplication.class, args);
    }
}

3. 創建用戶pojo和角色pojo

用戶pojo

public class SysUser implements UserDetails {
    private Integer id;
    private String username;
    private String password;
    private Integer status;
    private List<SysRole> roles = new ArrayList<>();

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public List<SysRole> getRoles() {
        return roles;
    }

    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }

    @JsonIgnore
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return true;
    }
}

角色pojo

public class SysRole implements GrantedAuthority {
    private Integer id;
    private String roleName;
    private String roleDesc;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public String getRoleDesc() {
        return roleDesc;
    }

    public void setRoleDesc(String roleDesc) {
        this.roleDesc = roleDesc;
    }

    //標記此屬性不做json處理
    @JsonIgnore
    @Override
    public String getAuthority() {
        return roleName;
    }
}

4. 編寫UserMapperRoleMapper

RoleMapper

public interface RoleMapper {
    @Select("select r.id,r.role_name roleName ,r.role_desc roleDesc " +
            "FROM sys_role r,sys_user_role ur " +
            "WHERE r.id=ur.rid AND ur.uid=#{uid}")
    public List<SysRole> findByUid(Integer uid);
}

UserMapper

public interface UserMapper {
    @Select("select * from sys_user where username=#{username}")
    @Results({
            @Result(id = true, property = "id", column = "id"),
            @Result(property = "roles", column = "id", javaType = List.class,
                    many = @Many(select = "com.example.mapper.RoleMapper.findByUid"))
    })
    public SysUser findByUsername(String username);

}

5. 編寫UserDetailService的實現類

UserService

public interface UserService extends UserDetailsService {

}

UserServiceImpl

@Service
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userMapper.findByUsername(username);
    }
}

6. 編寫 SpringSecurity配置類

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserService userService;
    @Autowired
    private PasswordEncoder passwordEncoder;


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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //所有資源必須授權后訪問
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()//指定認證頁面可以匿名訪問
                //關閉跨站請求防護
                .and()
                .csrf().disable();
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //UserDetailsService類
        auth.userDetailsService(userService)
                //加密策略
                .passwordEncoder(passwordEncoder);
    }

    //AuthenticationManager對象在OAuth2認證服務中要使用,提取放入IOC容器中
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

7. 編寫OAuth2授權配置類

@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private PasswordEncoder passwordEncoder;


    //從數據庫中查詢出客戶端信息
    @Bean
    public JdbcClientDetailsService clientDetailsService() {
        JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
        jdbcClientDetailsService.setPasswordEncoder(passwordEncoder);
        return jdbcClientDetailsService;
    }

    //token保存策略
    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }

    //授權信息保存策略
    @Bean
    public ApprovalStore approvalStore() {
        return new JdbcApprovalStore(dataSource);
    }

    //授權碼模式專用對象
    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    //指定客戶端登錄信息來源
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //從數據庫取數據
        clients.withClientDetails(clientDetailsService());

        // 從內存中取數據
//        clients.inMemory()
//                .withClient("baidu")
//                .secret(passwordEncoder.encode("12345"))
//                .resourceIds("product_api")
//                .authorizedGrantTypes(
//                        "authorization_code",
//                        "password",
//                        "client_credentials",
//                        "implicit",
//                        "refresh_token"
//                )// 該client允許的授權類型 authorization_code,password,refresh_token,implicit,client_credentials
//                .scopes("read", "write")// 允許的授權范圍
//                .autoApprove(false)
//                //加上驗證回調地址
//                .redirectUris("http://www.baidu.com");
    }

    //檢測token的策略
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.allowFormAuthenticationForClients()    //允許form表單客戶端認證,允許客戶端使用client_id和client_secret獲取token
                .checkTokenAccess("isAuthenticated()")     //通過驗證返回token信息
                .tokenKeyAccess("permitAll()")            // 獲取token請求不進行攔截
                .passwordEncoder(passwordEncoder);
    }

    //OAuth2的主配置信息
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .approvalStore(approvalStore())
                .authenticationManager(authenticationManager)
                .authorizationCodeServices(authorizationCodeServices())
                .tokenStore(tokenStore())
                .userDetailsService(userDetailsService);
    }
}

4. 測試

4.1 授權碼模式測試

在地址欄訪問地址
http://localhost:9001/oauth/authorize?response_type=code&client_id=baidu
跳轉到SpringSecurity默認認證頁面,提示用戶登錄個人賬戶【這里是sys_user表中的數據】

登錄成功后詢問用戶是否給予操作資源的權限,具體給什么權限。 Approve是授權,Deny是拒絕。
這里我們選擇readwrite都給予Approve

點擊 Authorize后跳轉到回調地址並獲取授權碼

使用授權碼到服務器申請通行令牌 token

測試攜帶通行令牌再次去訪問資源服務器資源路由

4.2 簡化模式測試

在地址欄訪問地址
http://localhost:9001/oauth/authorize?response_type=token&client_id=baidu
由於上面用戶已經登錄過了,所以無需再次登錄,其實和上面是有登錄步驟的,這時,瀏覽器直接返回了token

使用剛才生成的access_token訪問資源服務器

4.3 密碼模式測試

申請token

使用剛才生成的access_token訪問資源服務器

4.4 客戶端模式測試

申請token

使用剛才生成的access_token訪問資源服務器

感謝黑馬程序員提供的課程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM