当前位置: 代码迷 >> 综合 >> OAuth2.0入门(二)—— spring-security-oauth2入门例子JDBC存储(超详细版)上篇
  详细解决方案

OAuth2.0入门(二)—— spring-security-oauth2入门例子JDBC存储(超详细版)上篇

热度:86   发布时间:2023-11-24 10:56:23.0

前言:本文是OAuth2.0实践篇,阅读之前需要先掌握OAuth2.0基本原理,原理介绍见:OAuth2.0入门(一)—— 基本概念详解和图文并茂讲解四种授权类型     

       本章将采用微服务架构方式,将OAuth2-Demo拆分成三个模块:oauth2-authentication-server(作为授权认证中心)、oauth2-resource-server(作为资源服务器)、oauth-client(作为第三方应用,模拟如何获取Token访问资源)。

一、项目总结构

其中oauth2-demo是其他模块的Parent模块,定义了一些通用的Jar包。完整的pom文件如下:

<?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><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.6.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.scb</groupId><artifactId>oauth2-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>oauth2-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.1.11</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><modules><module>oauth2-authentication-server</module><module>oauth2-resource-server</module><module>oauth-client</module></modules></project>

二、oauth2-authentication-server 模块

oauth2-authentication-server 模块是作为全局的授权认证中心,pom文件如下:

<?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><parent><groupId>com.scb</groupId><artifactId>oauth2-demo</artifactId><version>0.0.1-SNAPSHOT</version><relativePath/> <!-- lookup parent from repository --></parent><artifactId>oauth2-authentication-server</artifactId><version>0.0.1-SNAPSHOT</version><name>oauth2-authentication-server</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!-- 阿里系的Druid依赖包 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.9</version></dependency><!-- Druid 依赖 log4j包 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.security.oauth</groupId><artifactId>spring-security-oauth2</artifactId><version>2.3.4.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

这里,除了导入spring-boot-starter-security和spring-security-oauth2认证框架外,还需要使用H2内存数据库来存储用户和角色信息及OAuth2的表。

先来看看application.yml文件:

spring:h2:console:path: /h2-consoleenabled: truesettings:web-allow-others: truejpa:generate-ddl: falseshow-sql: truehibernate:ddl-auto: nonedatasource:platform: h2schema: classpath:schema.sqldata: classpath:data.sqlurl: jdbc:h2:~/auth;AUTO_SERVER=TRUEusername: sapassword:type: com.alibaba.druid.pool.DruidDataSourcedruid:min-idle: 2initial-size: 5max-active: 10max-wait: 5000validation-query: select 1resources:static-locations: classpath:/templates/,classpath:/static/thymeleaf:prefix: classpath:/templates/suffix: .htmlmode: HTML5servlet:content-type: text/htmlcache: false
server:port: 8080
logging:pattern:level: debug

在yml文件中,我们定义了datasource为H2,并指定了schema、data文件,这样在项目运行时会执行相应的sql。其中schema.sql文件如下:

/* 1、存放用户认证信息及权限 */
drop table if exists authority;
CREATE TABLE authority (id  integer,authority varchar(255),primary key (id)
);drop table if exists credentials;
CREATE TABLE credentials (id  integer,enabled boolean not null,name varchar(255) not null,password varchar(255) not null,version integer,primary key (id)
);drop table if exists credentials_authorities;
CREATE TABLE credentials_authorities (credentials_id bigint not null,authorities_id bigint not null
);/* 2、oauth2官方建表语句 */
drop table if exists oauth_client_token;
create table oauth_client_token (token_id VARCHAR(255),token LONGBLOB,authentication_id VARCHAR(255),user_name VARCHAR(255),client_id VARCHAR(255)
);drop table if exists oauth_client_details;
CREATE TABLE oauth_client_details (client_id varchar(255) NOT NULL,resource_ids varchar(255) DEFAULT NULL,client_secret varchar(255) DEFAULT NULL,scope varchar(255) DEFAULT NULL,authorized_grant_types varchar(255) DEFAULT NULL,web_server_redirect_uri varchar(255) DEFAULT NULL,authorities varchar(255) DEFAULT NULL,access_token_validity integer(11) DEFAULT NULL,refresh_token_validity integer(11) DEFAULT NULL,additional_information varchar(255) DEFAULT NULL,autoapprove varchar(255) DEFAULT NULL
);drop table if exists oauth_access_token;
create table `oauth_access_token` (token_id VARCHAR(255),token LONGBLOB,authentication_id VARCHAR(255),user_name VARCHAR(255),client_id VARCHAR(255),authentication LONGBLOB,refresh_token VARCHAR(255)
);drop table if exists oauth_refresh_token;
create table `oauth_refresh_token`(token_id VARCHAR(255),token LONGBLOB,authentication LONGBLOB
);drop table if exists oauth_code;
create table oauth_code (code VARCHAR(255), authentication BLOB
);drop table if exists oauth_approvals;
create table oauth_approvals (userId VARCHAR(255),clientId VARCHAR(255),scope VARCHAR(255),status VARCHAR(10),expiresAt DATETIME,lastModifiedAt DATETIME
);

这里分为两部分,第一部分是自定义的用于存放用户凭证及授权的表。第二部分是官方建表语句:spring-security-oauth schema.sql,各个数据表说明如下:

  • oauth_client_details:存放client信息,主要操作类为JdbcClientDetailsService
  • oauth_client_token:存放client的Token信息。即通过client_credentials授权方式获得的Token。主要操作类为JdbcClientTokenServices
  • oauth_access_token:存放access token等,主要操作类为JdbcTokenStore
  • oauth_refresh_token:跟oath_access_token表类似,当client的grant type支持refresh token时才有记录。主要操作类为JdbcTokenStore
  • oauth_code:存放授权码(Authorization Code),即当client的grant type支持authorization_code时才有记录。主要操作类为JdbcAuthorizationCodeServices
  • oauth_approvals:存放用户授权client的信息,即当client的grant type支持authorization_code时才有记录。主要操作类为JdbcApprovalStore

字段及表详细说明如下,图片来源:https://blog.csdn.net/qq_34997906/article/details/89609297

oauth2 table

再来看一下data.sql文件,该文件主要是创建一些初始数据。

INSERT INTO authority  VALUES(1,'ROLE_OAUTH_ADMIN');
INSERT INTO authority VALUES(2,'ROLE_RESOURCE_ADMIN');
INSERT INTO authority VALUES(3,'ROLE_PRODUCT_ADMIN');/* password ==> password */
INSERT INTO credentials VALUES(1,true,'oauth_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials VALUES(2,true,'resource_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials  VALUES(3,true,'product_admin','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','0');
INSERT INTO credentials_authorities VALUES (1,1);
INSERT INTO credentials_authorities VALUES (2,2);
INSERT INTO credentials_authorities VALUES (3,3);/* password ==> password */
INSERT INTO oauth_client_details VALUES('curl_client','product_api', '$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO', 'read,write', 'client_credentials', 'http://localhost:7001/oauth2/accessToken', 'ROLE_PRODUCT_ADMIN', 7200, 0, null, 'true');
INSERT INTO oauth_client_details VALUES ('client_code','product_api','$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO','read,write','authorization_code,refresh_token','http://localhost:7001/oauth2/code','ROLE_PRODUCT_ADMIN',7200,72000,null,'true');
INSERT INTO oauth_client_details VALUES ('client_implicit',	'product_api'	,'$2a$10$5ze/vcFOsQBF1og.s.eQ0.8VdsUXh7zzul8VM0Dzcq/NKVNrD8ffO',	'read,write'	,'implicit',	'http://localhost:7001/oauth2/accessToken','ROLE_PRODUCT_ADMIN',7200,72000,null,'true');

因为项目使用了BCryptPasswordEncoder加密器,所以数据库的密码统一加密存储。

用户名 密码 权限
oauth_admin password ROLE_OAUTH_ADMIN
resource_admin password ROLE_RESOURCE_ADMIN
product_admin password ROLE_PRODUCT_ADMIN

接下来是Spring Security的配置部分。

1、Entity层

package com.scb.oauth2authenticationserver.entity;import lombok.Data;
import org.springframework.security.core.GrantedAuthority;import javax.persistence.*;
import java.io.Serializable;@Data
@Entity
@Table(name = "authority")
public class Authority implements GrantedAuthority, Serializable {private static final long serialVersionUID = -4737795841774495818L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "AUTHORITY")private String authority;
}
package com.scb.oauth2authenticationserver.entity;import lombok.Data;import javax.persistence.*;
import java.io.Serializable;
import java.util.List;@Data
@Entity
@Table(name = "credentials")
public class Credentials implements Serializable {private static final long serialVersionUID = -1408491858754963752L;@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@Column(name = "enabled")private boolean enabled;@Column(name = "name",nullable = false)private String name;@Column(name = "password",nullable = false)private String password;@Version@Column(name = "version",nullable = false)private Integer version;@ManyToMany(fetch = FetchType.EAGER)private List<Authority> authorities;
}

Entity层是实体层,映射数据表的字段。其中@Version注解是JPA实现的乐观锁机制。

2、Repository层

package com.scb.oauth2authenticationserver.repository;import com.scb.oauth2authenticationserver.entity.Credentials;
import org.springframework.data.jpa.repository.JpaRepository;public interface CredentialsRepository extends JpaRepository<Credentials,Long> {Credentials findByName(String name);
}

3、配置UserDetailsService

package com.scb.oauth2authenticationserver.service;import com.scb.oauth2authenticationserver.entity.Credentials;
import com.scb.oauth2authenticationserver.repository.CredentialsRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
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;@Slf4j
@Service
public class JdbcUserDetailsService implements UserDetailsService {@Autowiredprivate CredentialsRepository credentialsRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {Credentials credentials = credentialsRepository.findByName(username);if (credentials == null){throw new UsernameNotFoundException("User "+username+" cannot be found");}User user = new User(credentials.getName(),credentials.getPassword(),credentials.isEnabled(),true,true,true,credentials.getAuthorities());return user;}
}
UserDetailsService用于做Spring Security登录认证。关于Spring Security认证流程见:Spring Security 认证流程详解

4、SpringSecurityConfig

package com.scb.oauth2authenticationserver.config;import com.scb.oauth2authenticationserver.service.JdbcUserDetailsService;
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.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Bean@Overridepublic UserDetailsService userDetailsServiceBean() throws Exception {return new JdbcUserDetailsService();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/css/**","/js/**","/fonts/**");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http
//                .addFilter()
//                .antMatcher("oauth/authorize").authorizeRequests().antMatchers("/login","/logout.do").permitAll().antMatchers("/**").authenticated().and().formLogin().loginProcessingUrl("/login.do").usernameParameter("username").passwordParameter("password").loginPage("/login").and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout.do")).and().userDetailsService(userDetailsServiceBean());}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsServiceBean()).passwordEncoder(passwordEncoder());}}

SpringSecurityConfig模块一共有3个configure,分别是认证相关的AuthenticationManagerBuilder和web相关的WebSecurity、HttpSecurity。

  • AuthenticationManagerBuilder:用来配置全局的认证相关的信息,其实就是AuthenticationProvider和UserDetailsService。
  • WebSecurity:用来配置全局请求忽略规则(比如静态文件)、全局HttpFirewall、是否debug、全局SecurityFilterChain等。
  • HttpSecurity:用来配置具体的权限控制规则

5、Controller层

package com.scb.oauth2authenticationserver.controller;import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;import lombok.extern.slf4j.Slf4j;@Slf4j
@Controller
public class LoginController {@Autowiredprivate JdbcClientDetailsService clientDetailsService;@Autowiredprivate ApprovalStore approvalStore;@Autowiredprivate TokenStore tokenStore;@InitBinderprotected void init(HttpServletRequest request, ServletRequestDataBinder binder) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");dateFormat.setLenient(false);binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));}@RequestMapping("/login")public String loginPage() {tokenStore.findTokensByClientId("client_code").stream().forEach(accessToken -> log.info(accessToken.toString()));return "login";}@RequestMapping(value = "/logout", method = RequestMethod.GET)public String logoutPage(HttpServletRequest request, HttpServletResponse response) {Authentication auth = SecurityContextHolder.getContext().getAuthentication();if (auth != null) {new SecurityContextLogoutHandler().logout(request, response, auth);}return "redirect:/login?logout";}@RequestMapping("/")public ModelAndView root(Map<String, Object> model) {Authentication authentication = SecurityContextHolder.getContext().getAuthentication();log.info(authentication.getName());List<ClientDetails> clientDetails = clientDetailsService.listClientDetails();clientDetails.stream().forEach(clientDetails1 -> log.info(clientDetails1.toString()));List<Approval> approvals = clientDetails.stream().map(clientDetails1 -> approvalStore.getApprovals(authentication.getName(), clientDetails1.getClientId())).flatMap(Collection::stream).collect(Collectors.toList());approvals.stream().forEach(approval -> log.info(approval.toString()));model.put("approvals", approvals);model.put("clientDetails", clientDetails);return new ModelAndView("index", model);}@RequestMapping(value = "/approval/revoke", method = RequestMethod.POST)public String revokeApproval(@ModelAttribute Approval approval) {log.info(approval.toString());Boolean bool = approvalStore.revokeApprovals(Arrays.asList(approval));log.info(bool.toString());tokenStore.findTokensByClientIdAndUserName(approval.getClientId(), approval.getUserId()).forEach(tokenStore::removeAccessToken);return "redirect:/";}
}

LoginController定义了如下四个API:

  • /login:首先,通过TokenStore把client_id为client_code的access token打印出来(只是试试TokenStore的功能 :) ),然后返回用户的登录界面(“login”)。
  • /logout:从SecurityContext取用户的认证信息,若为空则直接logout,否则重定向到“/login?logout”界面(Spring默认的退出界面)。
  • /:根目录,通过ClientDetailsService查找所有的Client信息,再通过ApprovalStore查找当前登录用户所授权的所有的Client的Approval信息,最后封装进model里面,供View解析渲染。
  • /approval/revoke:该API通过ApprovalStore删除一个Approval信息,然后通过TokenStore删除通过该Approval获得的Token,最后重定向到根目录。

@InitBinder注解用于SpringMVC表单类型转换(比如这里对日期格式做格式化),具体转换在editor层,代码就不列出来了。有关@InitBinder注解的更多知识见:SpringMVC注解@initbinder解决类型转换问题

重头戏:OAuth2配置部分

package com.scb.oauth2authenticationserver.config;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetailsService;
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.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;import lombok.extern.slf4j.Slf4j;/*
Authorization Server Config*/
@Slf4j
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Autowired@Qualifier("jdbcUserDetailsService")private UserDetailsService userDetailsService;//    @Autowired
//    private AuthenticationManager authenticationManager;public AuthServerConfig() {super();}/*oauth_access_token Table*/@Beanpublic TokenStore tokenStore() {JdbcTokenStore tokenStore = new JdbcTokenStore(dataSource);log.info("Create TokenStore :: " + tokenStore);return tokenStore;}/*oauth_client_details Table用于配置client信息*/@Beanpublic JdbcClientDetailsService jdbcClientDetailsService() {JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);log.info("Create ClientDetailsService :: " + clientDetailsService);return clientDetailsService;}/*ApprovalStore:用于保存、检索user approvaloauth_approvals Table*/@Beanpublic ApprovalStore approvalStore() {JdbcApprovalStore approvalStore = new JdbcApprovalStore(dataSource);log.info("Create ApprovalStore :: " + approvalStore);return approvalStore;}/*oauth_code Table*/@Beanpublic AuthorizationCodeServices authorizationCodeServices() {JdbcAuthorizationCodeServices authorizationCodeServices = new JdbcAuthorizationCodeServices(dataSource);log.info("Create AuthorizationCodeServices :: " + authorizationCodeServices);return authorizationCodeServices;}/*AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security.allowFormAuthenticationForClients().checkTokenAccess("permitAll()");}/*ClientDetailsServiceConfigurer:用来配置客户端详情服务*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(jdbcClientDetailsService());}/*AuthorizationServerEndpointsConfigurer:来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints.approvalStore(approvalStore()).userDetailsService(userDetailsService)//.authenticationManager(authenticationManager).authorizationCodeServices(authorizationCodeServices()).tokenStore(tokenStore());}
}
授权服务器配置:使用 @EnableAuthorizationServer 来配置授权服务机制,并继承 AuthorizationServerConfigurerAdapter 该类重写 configure 方法定义授权服务器策略。

TokenStore总共有四种:

  • InMemoryTokenStore:将Token信息存入内存中,是OAuth2的默认实现方式。
  • JdbcTokenStore:将Token信息存入数据库
  • JwtTokenStore:将相关信息数据存入JWT中,实现无状态。需要引入 spring-security-jwt 库。通过JwtAccessTokenConverter进行编码及解码,同时需要添加jwtSigningKey,以此生成秘钥、进行签名。
  • RedisTokenStore:将Token信息存入Redis中

下面在列出OAuth2的一些默认端点:

  • /oauth/authorize:授权端点,用于grant_type为Authorization Code时,获取授权码。
  • /oauth/token:令牌端点,用于获取Access Token。
  • /oauth/confirm_access:用于grant_type为Authorization Code时,用户确认授权提交端点。
  • /oauth/error:授权服务错误信息端点。
  • /oauth/check_token:用于资源服务访问的令牌解析端点。
  • /oauth/token_key:在JwtTokenStore模式下提供公有密匙的端点。

其他

本文篇幅较长,故先只讲解了oauth2-authentication-server模块。剩下的内容见下一篇文章。

本文是基于springboot+springsecurity+oauth2整合(并用mysql数据库实现持久化客户端数据)的教程上进行二次开发的

 

下载项目:https://download.csdn.net/download/qq_37771475/12054521

 

  相关解决方案