前言
目前正在出一个SpringCloud进阶
系列教程,含源码解读
, 篇幅会较多, 喜欢的话,给个关注❤️ ~
前段时间拖更了,主要事情比较多和杂,不多废话了,直接给大家开整吧~ 本节重点是给大家介绍Oauth2,将会带大家从0到1搭建一个 SpringCloud
项目,以及整合Oauth2 Server
,实现基本的授权和认证功能。
什么是OAuth2.0
跟往常一样,在学习新知识前,首先明白它是啥?
我相信大部分人应该都对它都有了解,甚至使用过。如果你没有亲自搭建过这样的服务,但是在对接第三方平台的时候,首先需要去进行服务认证,在大多数的认证服务中,很多都是基于OAuth2.0
的。
首先我们需要明白这样一个概念, OAuth
,它是一个授权(Authorization)的开放标准,大家都可以基于这个标准去实现。OAuth基于HTTPS,以及APIs,Service应用使用```access_token````来进行身份验证。
它主要有OAuth 1.0
和OAuth 2.0
,二者不兼容。OAuth2.0 是目前广泛使用的版本,大部分说的OAuth
都是OAuth2.0
基本流程
它的基本流程是怎么样的呢?我们先看张图
上图中所涉及到的对象分别为:
-
Client
第三方应用,我们的应用就是一个Client
-
Resource Owner
资源所有者,即用户
-
Authorization Server
授权服务器,即提供第三方登录服务的服务器,如Github
-
Resource Server
拥有资源信息的服务器,通常和授权服务器属于同一应用
根据上图的信息,我们可以知道OAuth2
的基本流程为:
- 第三方应用请求用户授权。
- 用户同意授权,并返回一个凭证
(code)
- 第三方应用通过第二步的凭证
(code)
向授权服务器请求授权
- 授权服务器验证凭证
(code)
通过后,同意授权,并返回一个资源访问的凭证(Access Token)
。
- 第三方应用通过第四步的凭证
(Access Token)
向资源服务器请求相关资源。
- 资源服务器验证凭证
(Access Token)
通过后,将第三方应用请求的资源返回。
基本概念就介绍到这里,下面我们就一起整一下吧~
基本项目框架搭建
首先我们的准备工作,需要安装好Nacos
,如果还不会的同学可以参考我之前写的文章,这里默认大家都安装好了。本节呢,我们统一给大家尝试高版本,我这里的SpringBoot
版本是2.6+
,SpringCloud
是2021
,基本上都是比较新的版本了
下面新建一个maven
项目,父工程的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>pkq-cloud</groupId>
<artifactId>com.pkq.cloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-cloud-oauth2-server</module>
</modules>
<packaging>pom</packaging>
<properties>
<fate.project.version>1.0.0</fate.project.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<maven.plugin.version>3.8.1</maven.plugin.version>
<spring.boot.version>2.6.3</spring.boot.version>
<spring-cloud.version>2021.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.0.1.0</spring-cloud-alibaba.version>
<alibaba.nacos.version>1.4.2</alibaba.nacos.version>
<alibaba.sentinel.version>1.8.3</alibaba.sentinel.version>
<alibaba.dubbo.version>2.7.15</alibaba.dubbo.version>
<alibaba.rocketMq.version>4.9.2</alibaba.rocketMq.version>
<alibaba.seata.version>1.4.2</alibaba.seata.version>
<mybatis.plus.version>3.5.1</mybatis.plus.version>
<knife4j.version>3.0.2</knife4j.version>
<swagger.version>3.0.0</swagger.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- springBoot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springCloud -->
<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>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
复制代码
大家可以直接复制过去,下面我们新建一个模块叫spring-cloud-oauth2-server
,这个模块就是OAuth2
的授权服务中心,看下它的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>com.pkq.cloud</artifactId>
<groupId>pkq-cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-oauth2-server</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot2.4.x之后默认不加载bootstrap.yml文件,需要在pom里加上依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
<version>${spring-cloud-alibaba.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>${alibaba.nacos.version}</version>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.22</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- MyBatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.9.3</version>
</dependency>
<!--security -->
<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>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0</version>
</dependency>
<!--thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
</project>
复制代码
该模块下的配置文件bootstrap.yml
,不知道啥是bootstrap
配置文件的可以参考我之前的文章,在讲Nacos
的时候有给大家讲
server:
port: 10001
spring:
application:
name: oauth2-server
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: ********
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: 127.0.0.1
port: 6379
database: 2
cloud:
nacos:
discovery:
#注册中心地址
server-addr: 127.0.0.1:8848
config:
file-extension: yaml
#配置中心地址
server-addr: 127.0.0.1:8848
thymeleaf:
cache: false
mybatis-plus:
mapper-locations: classpath:mapper/*/*.xml,mapper/*.xml
global-config:
db-config:
id-type: auto
field-strategy: NOT_EMPTY
db-type: MYSQL
configuration:
map-underscore-to-camel-case: true
call-setters-on-nulls: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
复制代码
这里为了方便演示,直接把配置写到文件了,也可以放到Nacos
配置中心里,这里主要是当服务注册中心使用, Nacos
用到的sql
我放到项目的db
目录下了,需要的可自行获取
下面我们加个启动类,看是否启动正常~
/**
* @Author
* @Description 启动类
* @Date
*/
// @SpringCloudApplication 被弃用
@SpringBootApplication
public class OauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(OauthServerApplication.class, args);
}
}
复制代码
在新版本中@SpringCloudApplication
被弃用了,我们直接使用@SpringBootApplication
就好了,你会发现,啥都不加服务也会自动注册,说明新版本中已经比较完善了。@SpringCloudApplication
本质上也就是在@SpringBootApplicaiton
中加了几个注解
@Deprecated
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
public @interface SpringCloudApplication {
}
复制代码
启动一下,如果正常说明第一步我们就成功了~ 下面我们进入正题
搭建 OAuth2 Server
首先我们要去搭建一个认证服务配置
,Oauth2有多种模式,常见的有密码模式
还有授权码模式
,现在比较流行的就是授权码模式
,下面要带大家实现的就是基于授权码模式
的
认证服务配置
/**
* @Author qcl
* @Description Oauth2 认证服务配置
* @Date
*/
@AllArgsConstructor
@Configuration
@EnableAuthorizationServer
public class Oauth2ServerConfig extends AuthorizationServerConfigurerAdapter {
// 认证管理器
@Autowired
private AuthenticationManager authenticationManager;
// 数据源
@Autowired
private DataSource dataSource;
//密码加密方式
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 自定义身份认证
@Autowired
private UserServiceImpl userDetailsService;
@Bean
public ClientDetailsService jdbcClientDetailsService(){
//存储client信息
return new JdbcClientDetailsService(dataSource);
}
@Bean
public TokenStore tokenStore(){
// token存储
return new JdbcTokenStore(dataSource);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
// 授权码模式
return new JdbcAuthorizationCodeServices(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置 client信息从数据库中取
clients.withClientDetails(jdbcClientDetailsService());
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())//token存储方式
.authenticationManager(authenticationManager)// 开启密码验证,由 WebSecurityConfigurerAdapter
.userDetailsService(userDetailsService)// 读取验证用户信息
.authorizationCodeServices(authorizationCodeServices())
.setClientDetailsService(jdbcClientDetailsService());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
// 配置Endpoint,允许请求
security.tokenKeyAccess("permitAll()") // 开启/oauth/token_key 验证端口-无权限
.checkTokenAccess("isAuthenticated()") // 开启/oauth/check_token 验证端口-需权限
.allowFormAuthenticationForClients()// 允许表单认证
.passwordEncoder(passwordEncoder()); // 配置BCrypt加密
}
}
复制代码
这里我们使用基于持久化
的配置,相关注释已经加上了,那么它的数据存在哪呢?它是存在数据库中,下面我们需要准备几张表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`token` blob NULL,
`authentication_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`user_name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`client_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`authentication` blob NULL,
`refresh_token` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储生成的access_token' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_access_token
-- ----------------------------
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL,
`resource_ids` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`client_secret` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`scope` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`authorities` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`access_token_validity` int NULL DEFAULT NULL,
`refresh_token_validity` int NULL DEFAULT NULL,
`additional_information` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL,
`autoapprove` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'false',
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储客户端的配置信息' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_client_details
-- ----------------------------
INSERT INTO `oauth_client_details` VALUES ('clientId1', '1', '$2a$10$WFDhpNunaH0aPlPQp33q8OAlqot6NANran7HeP/DSSWt1kVRvF2d2', 'all', 'password,refresh_token,authorization_code,implicit,client_credentials', 'https://www.baidu.com', 'ROLE_ADMIN', 604800, 1209600, '{\"info\": \"test\"}', 'false');
-- ----------------------------
-- Table structure for oauth_code
-- ----------------------------
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (
`code` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储服务端系统生成的code的值' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL,
`token` blob NULL,
`authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '存储刷新令牌的refresh_token' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of oauth_refresh_token
-- ----------------------------
SET FOREIGN_KEY_CHECKS = 1;
复制代码
默认给大家已经插入了一条客户端的配置,后边我们会用到它
在代码中,我们可以看到导入了一个UserDetailsService
,这个类需要我们实现它
/**
* @Author qcl
* @Description 用户信息
* @Date
*/
@Slf4j
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
UserInfo userInfo = sysUserService.getUserByUserName(userName);
log.warn("user: " + userInfo.getUsername());
if (userInfo == null){
throw new UsernameNotFoundException("用户不存在: " + userName);
}
List<GrantedAuthority> authorities=new ArrayList<>();
//获取用户权限
List<String> permissions = userInfo.getPermissions();
permissions.forEach(permission->{
authorities.add(new SimpleGrantedAuthority("ROLE_" + permission));
});
// 这里一定要基于 BCrypt 加密,不然会不通过
UserDetails user = new User(userInfo.getUsername(), new BCryptPasswordEncoder().encode(userInfo.getPassword()), authorities);
log.warn(user.toString());
return user;
}
}
复制代码
它主要是用来验证和存储用户信息的,场景就是用户在进行登录授权的时候会走到这里边,我们可以在这进行用户身份校验,SysUserService
也是我们自己实现的,用来获取用户信息,这里为了方便演示,直接模拟了
@Service
public class SysUserServiceImpl implements SysUserService {
@Override
public UserInfo getUserByUserName(String userName) {
ArrayList<String> list = new ArrayList<>();
list.add("ADMIN");
UserInfo user = new UserInfo();
user.setUserId("1111");
user.setPassword("123321");
user.setUsername(userName);
user.setPermissions(list);
return user;
}
}
复制代码
这里给大家提示下可能遇到的坑, 就是userInfo.getPassword()
, 如果你是从数据库读的,如果使用了BCryptPasswordEncoder
加密过的,那么可以,如果不是的话,你就需要BCryptPasswordEncoder
加密一下,不过这样不大安全,推荐是大家在数据库加密,我这里为了方便给大家演示
开启Spring Security配置
下面我们整一下安全配置
/**
* @Author
* @Description 开启Spring Security配置
* @Date
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/oauth/check_token");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.antMatchers("/view/**").permitAll()
.anyRequest().authenticated();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
复制代码
认证流程
现在我们就可以启动它了,然后到浏览器访问http://localhost:10001/oauth/authorize?client_id=clientId1&response_type=code&scope=all&redirect_uri=https://www.baidu.com
有几个参数,给大家解释一下:
-
/oauth/authorize
这个是Ouath2默认开启的规范接口,通过它进行客户端验证
-
client_id
客户端ID, 也就是分配给第三方的认证ID,
-
response_type=code
返回的类型code,也就是我们开启的是授权码模式
-
scope
授权范围, 需要和我们数据库配置的一致
-
redirect_uri
认证成功后重定向的地址,它会携带code
参数
说了这些,一开始接触的朋友,可能还是不大清除,下面给一张图给大家体会一下:
如果有朋友接过h5微信的登录,相必会深有体会
下面,我们就去访问它。当我们在浏览器输入地址的时候会自动跳转到/login
的地址页面。这个页面是框架自带的,有时候,我们有很多个性化的需求需要自定义,下面就带大家整一下自定义的登录和授权页面
自定义授权和登录页
配置也很简单,我们只需要修改WebSecurityConfig
的配置
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.antMatchers("/view/**", "/user/**").permitAll()
.anyRequest().authenticated();
http.formLogin()
// 自定义处理登录逻辑的地址login
.loginProcessingUrl("/login")
// 自定义登录页面
.loginPage("/view/login")
.permitAll()
.and()
.csrf().disable().httpBasic();
}
复制代码
这里,我已经准备好了模板,我们使用thymeleaf
简单的给大家演示一下
/**
* @Author
* @Description
* @Date
*/
@Controller
@RequestMapping("/view")
public class ViewController {
@RequestMapping("/login")
public String loginView(Model model) {
return "oauth-login";
}
}
复制代码
- templates/oauth-login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www/thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div>
<h1>授权登录页</h1>
<br/>
<form name="loginForm" method="post" action="/login">
用户名: <input type="text" name="username" placeholder="用户名" />
<br/>
密码: <input type="password" name="password" placeholder="密码" />
<button type="submit">点击登录</button>
</form>
</div>
</body>
</html>
复制代码
因为我们引入的是```spring-boot-starter-thymeleaf````,所以默认给我们配置好了,直接添加html就可以了,下面我们再自定义一下授权页,授权页就是比如你用qq登录了网站, 你扫码成功了或者密码输入整个了,这时候会提示获取你的信息,你同意了才可以继续操作
自定义授权页,相对复杂一点,如果对流程和源码不咋熟的话,有点困难
首先需要我们改一下Oauth2ServerConfig
的配置,因为授权在这里
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())//token存储方式
.authenticationManager(authenticationManager)// 开启密码验证,由 WebSecurityConfigurerAdapter
.userDetailsService(userDetailsService)// 读取验证用户信息
.authorizationCodeServices(authorizationCodeServices())
// 自定义授权跳转
.pathMapping("/oauth/confirm_access", "/custom/confirm_access")
// 自定义异常跳转
.pathMapping("/oauth/error", "/view/oauth/error")
.setClientDetailsService(jdbcClientDetailsService());
}
复制代码
默认下是"/oauth/confirm_access
,我们替换成自定义的/custom/confirm_access
,同样的自定义异常的页面也是可以的。下一步,我们需要加个控制器AuthorizeController
@SessionAttributes("authorizationRequest")
@Controller
public class AuthorizeController {
@RequestMapping("/custom/confirm_access")
public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception {
AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
ModelAndView view = new ModelAndView();
view.setViewName("oauth-authorize");
view.addObject("clientId", authorizationRequest.getClientId());
view.addObject("scopes",authorizationRequest.getScope());
return view;
}
}
复制代码
紧接着就是模板文件 oauth-authorize.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www/thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>授权页</title>
</head>
<body>
<div>
<h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'"></h3>
<p>昵称,头像和性别</p>
授权后表明你已同意 <a href="#boot" style="color: #E9686B">服务协议</a>
<form method="post" action="/oauth/authorize">
<input type="hidden" name="user_oauth_approval" value="true">
<div th:each="item:${scopes}">
<input type="radio" th:name="'scope.'+${item}" value="true" checked>同意
<input type="radio" th:name="'scope.'+${item}" value="false" >拒绝
</div>
<input name="authorize" value="同意/授权" type="submit">
</form>
</div>
</body>
</html>
复制代码
这一步,你可以向用户展示信息,这里就给大家简单写写,我们主要体验功能, 好,有了这些准备之后,我们可以正式进入测试环节了
测试环节
首先访问http://localhost:10001/oauth/authorize?client_id=clientId1&response_type=code&scope=all&redirect_uri=https://www.baidu.com
,然后自动跳转到/view/login
,输入完用户名和密码会跳转到授权页
也就是/custom/confirm_access
点击同意之后会跳转到https://www.baidu.com?code=xxx
,会携带code
参数,下面我们就可以通过code
获取access_token
了,获取token````的地址,
http://localhost:10001/oauth/token```,使用```POST```请求,因为我们开启了``` .allowFormAuthenticationForClients()// 允许表单认证```,所以可以进行表单请求,如果没开启的话,是没法的。
我们需要拿着secret
去请求,也就是数据库存储的client_secret
,这个一般由服务提供者提供,也就是所谓的秘钥
成功之后会返回```access_token``,到这里我们就算搭建成功了
说一下这里大家可能遇到的坑,如果你遇到了not like BCrypt
的报错,然后明明你的代码配置都对,可怎么改都没有用,八成是你的secret
不对,首先排查你传的secret
,这个时没有BCrypt
过的,其次就是数据库存储的secret
这里不能明文存储,需要是加密过的,这个也很简单,我们本地加密一下存进去就行了
public class BCryptTest {
public static void main(String[] args) {
System.out.println(new BCryptPasswordEncoder().encode("123321"));
}
}
复制代码
结束语
本节到这里就结束啦,希望可以对大家有所帮助。