目录
一、引言
1. 详解
2. 实现流程
3. 实例说明
二、实例案例
1. 数据表
2. 项目结构
3. 所需依赖及生成
4. 授权处理
每篇一获
授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
适用场景 :
1. Web应用程序:当客户端是一个运行在Web服务器上的应用程序时,授权码模式是一个常见的选择。用户在Web应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。
2. 移动应用程序:对于需要访问受保护资源的移动应用程序,授权码模式也是一个很好的选择。用户可以在移动应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。
3. 第三方应用程序:当一个第三方应用程序需要代表用户访问受保护资源时,授权码模式也可以使用。用户可以在授权服务器上进行登录和授权,然后第三方应用程序使用授权码来获取访问令牌。
总之,授权码模式适用于需要在用户和客户端之间建立信任关系,并且需要通过授权码来获取访问令牌的场景。它是一种相对安全的授权方式,适用于多种不同类型的应用程序。
主要流程 :
用户访问客户端,客户端将用户重定向到授权服务器。
用户在授权服务器上登录并同意授权客户端请求的权限。
授权服务器将用户重定向回客户端,并附上一个授权码。
客户端收到授权码后,使用该授权码向授权服务器请求访问令牌。
客户端向授权服务器发送包含授权码的请求,并提供客户端凭证(客户端ID和客户端密钥)。
授权服务器验证客户端凭证和授权码,如果验证通过,授权服务器将颁发访问令牌给客户端。
客户端使用访问令牌向受保护资源服务器请求受保护资源。
通过以上流程,客户端可以通过授权码模式获取访问令牌,并使用该访问令牌访问受保护资源。这种方式可以保护用户的敏感信息,同时也可以保护客户端的凭证信息。
请求示例:
步骤 1 :客户端申请认证的URI
https://www.cloudjun.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL
&scope=read&state=xxx
参数说明:
参数类型 | 说明 |
---|---|
response_type | 授权类型,必选项,此处的值固定为"code" |
client_id | 客户端的ID,必选项 |
redirect_uri | 重定向URI,认证服务器接受请求之后的调转连接,可以根据这个连接将生成的授权码回传,必选项 |
scope | code发送给资源服务器申请的权限范围,可选项 |
state | 任意值,认证服务器会原样返回,用于抵制CSRF(跨站请求伪造)攻击。 |
步骤 3 :服务器回应客户端的URI
https://client.cloudjun.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xxx
参数说明:
参数类型 | 说明 |
---|---|
code | 授权码,必选项。授权码有效期通常设为10分钟,一次性使用。该码与客户端ID、重定向URI以及用户,是一一对应关系。 |
state | 原样返回客户端传的该参数的值 |
步骤 4 :客户端向认证服务器申请令牌
https://www.cloudjun.com/oauth/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&
grant_type=authorization_code&code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
参数说明:
参数类型 | 说明 |
---|---|
client_id | 表示客户端ID,必选项 |
client_secret | 表示安全参数,只能在后端发请求 |
grant_type | 表示使用的授权模式,必选项,此处的值固定为"authorization_code" |
code | 表示上一步获得的授权码,必选项 |
redirect_uri | 表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致 |
步骤 5 :响应步骤(4)的数据
{
"access_token":访问令牌,
"token_type":"bearer",
"expires_in":过期时间,
"refresh_token":"REFRESH_TOKEN",
"scope":"read",
"uid":用户ID,
"info":{...}
}
参数说明:
参数类型 | 说明 |
---|---|
access_token | 访问令牌,必选项 |
token_type | 令牌类型,该值大小写不敏感,必选项 |
expires_in | 过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间 |
refresh_token | 更新令牌,用来获取下一次的访问令牌,可选项 |
scope | 权限范围,如果与客户端申请的范围一致,此项可省略 |
以下代码及操作有不理解的可以看我所写的"实现流程"及"实例说明"进行代码结合理解
首先需要创建数据表,根据以下表格及字段创建即可,或有更好的也可
如何我们使用Mybatis-plus来生成代码,编写一个代码生成来。
这个代码生成类,两个小项目中都需要有。
MySQLGenerator
package com.wx.server.config;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@Slf4j
public class MySQLGenerator {
private final static String URL = "jdbc:mysql://localhost:3306/oauth?serverTimezone=GMT";
private final static String USERNAME = "root";
private final static String PASSWORD = "123456";
private final static DataSourceConfig.Builder DATA_SOURCE_CONFIG =
new DataSourceConfig.Builder(URL, USERNAME, PASSWORD);
public static void main(String[] args) {
FastAutoGenerator.create(DATA_SOURCE_CONFIG)
.globalConfig(
(scanner, builder) ->
builder.author(scanner.apply("请输入作者名称?"))
.outputDir(System.getProperty("user.dir") + "\\wx-server\\src\\main\\java")
.commentDate("yyyy-MM-dd")
.dateType(DateType.TIME_PACK)
)
.packageConfig((builder) ->
builder.parent("com.wx.server")
.entity("pojo")
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml")
.pathInfo(Collections.singletonMap(OutputFile.xml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))
)
.injectionConfig((builder) ->
builder.beforeOutputFile(
(a, b) -> log.warn("tableInfo: " + a.getEntityName())
)
)
.strategyConfig((scanner, builder) ->
builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all")))
.addTablePrefix("tb_", "t_", "lay_", "meeting_", "sys_", "t_medical_", "oath_")
.entityBuilder()
.enableChainModel()
.enableLombok()
.enableTableFieldAnnotation()
.controllerBuilder()
.enableRestStyle()
.enableHyphenStyle()
.build()
)
.templateEngine(new FreemarkerTemplateEngine())
.execute();
}
protected static List getTables(String tables) {
return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));
}
}
Oauth大项目中的pom.xml依赖有以下 :
4.0.0
org.example
Oauth
1.0-SNAPSHOT
pom
zking-server
wx-server
0.31
1.8
UTF-8
UTF-8
2.4.1
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
com.baomidou
mybatis-plus-boot-starter
3.5.2
com.baomidou
mybatis-plus-generator
3.5.2
org.springframework.boot
spring-boot-starter-data-redis
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-freemarker
com.github.yitter
yitter-idgenerator
1.0.6
org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import
org.apache.oltu.oauth2
org.apache.oltu.oauth2.client
${oauth2.version}
org.apache.oltu.oauth2
org.apache.oltu.oauth2.authzserver
${oauth2.version}
org.apache.oltu.oauth2
org.apache.oltu.oauth2.resourceserver
${oauth2.version}
在wx-server 的项目中,生成根据数据表中的oath_user,oath_company表
其中项目的pom.xml依赖有以下 :
4.0.0
com.example
wx-server
0.0.1-SNAPSHOT
org.apache.maven.plugins
maven-compiler-plugin
8
Oauth
org.example
1.0-SNAPSHOT
org.apache.oltu.oauth2
org.apache.oltu.oauth2.authzserver
org.apache.oltu.oauth2
org.apache.oltu.oauth2.resourceserver
io.jsonwebtoken
jjwt
0.9.1
com.auth0
java-jwt
3.4.0
在wx-server 的项目中
依赖导入完成后就生成代码。oath_user,oath_company
不知道如何生成代码可以看我博客中写的 :
Mybatis-plus是使用,告别繁琐的CRUD编写,自动生成直接使用https://blog.csdn.net/SAME_LOVE/article/details/134979107?spm=1001.2014.3001.5501 其 搭建使用 中的 生成。
在wx-server 的项目中 的application.yml文件配置
spring:
freemarker:
suffix: .ftl
template-loader-path: classpath:/templates/
enabled: true
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/oauth?serverTimezone=GMT
redis:
host: localhost
port: 6379
database: 0
server:
port: 9999
在 zking-server 的项目中,生成根据数据表中的 zking_user 表
其中项目的pom.xml依赖有以下 :
4.0.0
com.example
zking-server
0.0.1-SNAPSHOT
org.apache.maven.plugins
maven-compiler-plugin
8
Oauth
org.example
1.0-SNAPSHOT
org.apache.oltu.oauth2
org.apache.oltu.oauth2.client
在 zking-server 的项目中 , 依赖导入完成后就生成代码。zking_user
在 zking-server 的项目中 , 之后在实体包中创建一个User实体 :
package com.zking.server.pojo; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import java.io.Serializable; @Getter @Setter @Accessors(chain = true) public class User implements Serializable { private static final long serialVersionUID = 1L; private String id; private String nickName; private String realName; private String bankId; private String openId; private String account; private String password; private String avatar; }
在 zking-server 的项目中 的application.yml文件配置
spring:
freemarker:
suffix: .ftl
template-loader-path: classpath:/templates/
enabled: true
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/oauth?serverTimezone=GMT
server:
port: 8080
在 zking-server 的项目中的resources文件中创建templates文件,再分别创建login.ftl和
phone.ftl
login.ftl
Document
phone.ftl
Document
在 wx-server 的项目中的resources文件中创建templates文件,再分别创建login.ftl和
error.ftl及auth.ftl
login.ftl
Document
error.ftl
Document
请检查贵公司是否与我们有合作
也可能你拒绝了授权
auth.ftl
Document
在 wx-server 的项目中,创建一个JwtUtils。
JwtUtils
package com.wx.server.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Data
public class JwtUtils {
private String secret = "wx1314";
private Long expiration = Integer.valueOf(1000 * 60 * 60).longValue();
/**
* 从数据生成令牌
*/
private String generateToken(Map claims) {
Date expirationDate = new Date(System.currentTimeMillis() + expiration);
return
Jwts
.builder()
.setClaims(claims)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 获取令牌中的数据
*/
public Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成令牌(携带了用户名与签发时间)
*/
public String generateToken(String openId) {
Map claims = new HashMap<>(2);
claims.put(Claims.SUBJECT, openId);
claims.put(Claims.ISSUED_AT, new Date());
return generateToken(claims);
}
/**
* 获取令牌中的openId
*/
public String getOpenIdFromToken(String token) {
long id;
try {
Claims claims = getClaimsFromToken(token);
return claims.getSubject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 判断令牌是否过期
public Boolean isTokenExpired(String token) {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
}
*/
/**
* 刷新令牌
public String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put(Claims.ISSUED_AT, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
*/
/**
* 验证令牌
public Boolean validateToken(String token, UserDetails userDetails) {
User user = (User) userDetails;
String username = getUsernameFromToken(token);
return !isTokenExpired(token) && username.equals(user.getUsername());
}
*/
}
在 wx-server 的项目中,创建一个AuthController,处理登入和获得客户端的信息及回应处理转发。
AuthController
package com.wx.server.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.yitter.idgen.YitIdHelper;
import com.wx.server.pojo.Company;
import com.wx.server.pojo.User;
import com.wx.server.service.ICompanyService;
import com.wx.server.service.IUserService;
import com.wx.server.util.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Controller
@RequestMapping("/auth")
@Slf4j
@SuppressWarnings("all")
public class AuthController {
@Autowired
private ICompanyService companyService;
@Autowired
private IUserService userService;
@Autowired
private RedisTemplate redisTemplate;
@GetMapping("/code")
public String sendCode(HttpServletRequest request, Model model) {
try {
//解析请求
OAuthAuthzRequest oathReq = new OAuthAuthzRequest(request);
//获取到客户端的id
String clientId = oathReq.getClientId();
if (clientId == null) return "redirect:/error";
Company company = companyService.getOne(new QueryWrapper().lambda().eq(Company::getClientId, clientId), false);
//如果客户端跟我们没有合作
if (company == null) return "redirect:/error";
// 将oathReq.getRedirectURI()的值赋值给model中的redirect_uri
model.addAttribute("redirect_uri", oathReq.getRedirectURI());
// 将company的值赋值给model中的company_info
model.addAttribute("company_info", company);
} catch (Exception e) {
return "error";
}
//跳到认证登录页
return "login";
}
@PostMapping("/doLogin")
public String doLogin(User user, String redirect_uri, Model model) {
//用户登录
User one = userService.getOne(new QueryWrapper().lambda()
.eq(User::getAccount, user.getAccount())
.eq(User::getPassword, user.getPassword()));
//登录失败
if (one == null) {return "redirect:/error";}
//生成一个雪花id
long snowId = YitIdHelper.nextId();
//将需要的数据放到缓存中
//1.用作等会需要的判断,判断用户进行授权的请求是否来源与后端
//2.将用户的数据放入到缓存中,避免在前端暴露
redisTemplate.opsForValue().set("request:" + snowId, redirect_uri, 5, TimeUnit.MINUTES);
//将one.getId()存储到redis中,以snowId为key
redisTemplate.opsForValue().set("user:" + snowId, one.getId());
//将snowId存储到model中
model.addAttribute("snowId", snowId + "");
//返回auth页面
return "auth";
}
@GetMapping("/doAuth")
public String doAuth(HttpServletRequest request, String snowId) throws Exception {
//回调路径
Object obj = redisTemplate.opsForValue().get("request:" + snowId);
if (obj == null) {
return "redirect:/error";
}
//request:123456
String userId = redisTemplate.opsForValue().get("user:" + snowId).toString();
String code = getCode();
redisTemplate.opsForValue().set("code:" + code, userId, 5, TimeUnit.MINUTES);
//获取构建响应的对象
OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_OK);
builder.setCode(code);
String redirectURI = obj.toString();
OAuthResponse oauthResp = builder.location(redirectURI).buildQueryMessage();
//形成路径路径拼接效果 http://localhost:80/client/callback?code=xx
String uri = oauthResp.getLocationUri();
// redirect:/client/callback?code=xx
return "redirect:" + uri;
}
/**
* 生成授权码方法
*/
public String getCode() {
Random r = new Random();
String code = "";
for (int i = 0; i < 8; ++i) {
int temp = r.nextInt(52);
char x = (char) (temp < 26 ? temp + 97 : (temp % 26) + 65);
code += x;
}
return code;
}
@PostMapping("/token")
public HttpEntity getAccessToken(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException {
//OAuthTokenRequest解析请求
OAuthTokenRequest tokenReq = new OAuthTokenRequest(request);
//获得客户端的信息
String clientId = tokenReq.getClientId();
String clientSecret = tokenReq.getClientSecret();
Company company = companyService.getOne(new QueryWrapper().lambda().eq(Company::getClientId, clientId).eq(Company::getClientSecret, clientSecret), false);
//去数据库做查询
if (company != null) {
//做授权码的判断
String code = tokenReq.getCode();
//将授权码带入到缓存中查看是否有对应的数据
String userId = redisTemplate.opsForValue().get("code:" + code).toString();
if(userId==null){
System.out.println("授权码可能过期或者伪造了");
return null;
}
String openId = userService.getById(userId).getOpenId();
String token = new JwtUtils().generateToken(openId);
//构造保护令牌的响应对象
OAuthResponse oAuthResponse = OAuthASResponse
.tokenResponse(HttpServletResponse.SC_OK)
.setAccessToken(token)
.buildJSONMessage();
return new ResponseEntity(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus()));
}
return null;
}
@GetMapping("/userinfo")
@ResponseBody
public Object getUserInfo(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException {
//OAuthAccessResourceRequest解析请求
OAuthAccessResourceRequest oAuthAccessResourceRequest = new OAuthAccessResourceRequest(request, ParameterStyle.HEADER);
//请求头中获取令牌
String token = oAuthAccessResourceRequest.getAccessToken();
//判断令牌是否是我发给你的
String openId = new JwtUtils().getOpenIdFromToken(token);
if (openId == null) {
return null;
}
User user = userService.getOne(new QueryWrapper().lambda().eq(User::getOpenId, openId));
return user;
}
}
在 zking-server 的项目中,创建一个GetAuthorizationController,服务器授权的跳转及授权码的回调
GetAuthorizationController
package com.zking.server.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zking.server.pojo.User;
import com.zking.server.pojo.ZkingUser;
import com.zking.server.service.IZkingUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/client")
@SuppressWarnings("all")
@Slf4j
public class GetAuthorizationController {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private IZkingUserService zkingUserService;
private static String CLIENT_ID = "123";
private static String CLIENT_SECRET = "123";
@GetMapping("/login")
public String login() {
return "login";
}
/**
* 跳往微信服务器页面
*/
@GetMapping("/wx")
public String getCode() throws OAuthSystemException {
OAuthClientRequest oAuthClientRequest = OAuthClientRequest
.authorizationLocation("code")
.setClientId("ai")
.setRedirectURI("http://localhost:8080/client/callbackCode")
.setResponseType("code")
.buildQueryMessage();
String uriString = oAuthClientRequest.getLocationUri();
//重定向到资源所有者,获取验证码
return "redirect:http://localhost:9999/auth/" + uriString;
}
/**
* 授权码回调方法
*/
@RequestMapping("/callbackCode")
public String callbackCode(HttpServletRequest request) throws Exception {
//获得授权码
String code = request.getParameter("code");
//根据授权码获得令牌
//OAuthClientRequest封装了所有的参数
//OAuthClient发起请求
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthClientRequest tokenRequest = OAuthClientRequest
.tokenLocation("http://localhost:9999/auth/token")
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setCode(code)
.setRedirectURI("http://localhost:8080/client/callbackCode")
.buildQueryMessage();
//通过Code,向认证服务器申请令牌
OAuthJSONAccessTokenResponse tokenResp = oAuthClient.accessToken(tokenRequest, OAuth.HttpMethod.POST);
//获取令牌 21713781312371token
String accessToken = tokenResp.getAccessToken();
OAuthClientRequest userInfoRequest =
new OAuthBearerClientRequest("http://localhost:9999/auth/userinfo")
.setAccessToken(accessToken)
.buildHeaderMessage();
OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
String json = resourceResponse.getBody();
User user = objectMapper.readValue(json, User.class);
//获得用户的openId
String openId = user.getOpenId();
//判断我数据库是否有这个人
ZkingUser one = zkingUserService.getOne(new QueryWrapper().lambda().eq(ZkingUser::getOpenId, openId), false);
//判断
if(one==null){
return "phone";
}
return "index";
}
@GetMapping("/bind")
@ResponseBody
public String bind(ZkingUser user) {
zkingUserService.save(user);
return "yes";
}
}
在项目中使用授权码模式可以带来以下收获:
1. 安全性:授权码模式通过将访问令牌的获取过程分为两步,有效减少了访问令牌在传输过程中被窃取的风险。用户的凭证信息不会直接暴露给客户端,提高了安全性。
2. 用户体验:授权码模式可以让用户在授权服务器上进行登录和授权,而不是直接在客户端中输入凭证信息。这种方式可以提供更好的用户体验,同时也可以减少客户端的安全风险。
3. 灵活性:授权码模式可以适用于多种类型的应用程序,包括Web应用程序、移动应用程序和第三方应用程序。这种灵活性可以使得授权码模式成为一个通用的授权方式,适用于各种不同场景的项目。
4. 标准化:授权码模式是OAuth 2.0标准中定义的一种授权方式,使用它可以使得项目符合OAuth 2.0的标准规范,提高了项目的可维护性和可扩展性。
总之,使用授权码模式可以提高项目的安全性和用户体验,同时也可以使得项目符合标准规范,具有更好的灵活性和通用性。