【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)

【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)授权码 authorizatio 方式 指的是第三方应用先申请一个授权码 然后再用该码获取令牌

大家好,欢迎来到IT知识分享网。

目录

一、引言

1. 详解

2. 实现流程

3. 实例说明 

二、实例案例

1. 数据表

2. 项目结构

3. 所需依赖及生成 

4. 授权处理

每篇一获


一、引言

1. 详解

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。

适用场景 : 

1. Web应用程序:当客户端是一个运行在Web服务器上的应用程序时,授权码模式是一个常见的选择。用户在Web应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。

2. 移动应用程序:对于需要访问受保护资源的移动应用程序,授权码模式也是一个很好的选择。用户可以在移动应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。

3. 第三方应用程序:当一个第三方应用程序需要代表用户访问受保护资源时,授权码模式也可以使用。用户可以在授权服务器上进行登录和授权,然后第三方应用程序使用授权码来获取访问令牌。

总之,授权码模式适用于需要在用户和客户端之间建立信任关系,并且需要通过授权码来获取访问令牌的场景。它是一种相对安全的授权方式,适用于多种不同类型的应用程序。

2. 实现流程

【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)

主要流程 :

  1. 用户访问客户端,客户端将用户重定向到授权服务器。
  2. 用户在授权服务器上登录并同意授权客户端请求的权限。
  3. 授权服务器将用户重定向回客户端,并附上一个授权码。
  4. 客户端收到授权码后,使用该授权码向授权服务器请求访问令牌。
  5. 客户端向授权服务器发送包含授权码的请求,并提供客户端凭证(客户端ID和客户端密钥)。
  6. 授权服务器验证客户端凭证和授权码,如果验证通过,授权服务器将颁发访问令牌给客户端。
  7. 客户端使用访问令牌向受保护资源服务器请求受保护资源。

通过以上流程,客户端可以通过授权码模式获取访问令牌,并使用该访问令牌访问受保护资源。这种方式可以保护用户的敏感信息,同时也可以保护客户端的凭证信息。

3. 实例说明 

请求示例:

  • 步骤 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=SplxlOBeZYbYS6WxSbIA&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 令牌类型,该值大小写不敏感,必选项
expires_in 过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间
refresh_token 更新令牌,用来获取下一次的访问令牌,可选项
scope 权限范围,如果与客户端申请的范围一致,此项可省略

二、实例案例

以下代码及操作有不理解的可以看我所写的”实现流程”及”实例说明”进行代码结合理解

1. 数据表

首先需要创建数据表,根据以下表格及字段创建即可,或有更好的也可

【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)

2. 项目结构

【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)

如何我们使用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 = ""; 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<String> getTables(String tables) { return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(",")); } }

 Oauth大项目中的pom.xml依赖有以下 : 

<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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>org.example</groupId> <artifactId>Oauth</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>zking-server</module> <module>wx-server</module> </modules> <properties> <oauth2.version>0.31</oauth2.version> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.4.1</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>com.github.yitter</groupId> <artifactId>yitter-idgenerator</artifactId> <version>1.0.6</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.client</artifactId> <version>${oauth2.version}</version> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.authzserver</artifactId> <version>${oauth2.version}</version> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId> <version>${oauth2.version}</version> </dependency> </dependencies> </dependencyManagement> </project>

3. 所需依赖及生成 

在wx-server 的项目中,生成根据数据表中的oath_user,oath_company表

其中项目的pom.xml依赖有以下 : 

<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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> <groupId>com.example</groupId> <artifactId>wx-server</artifactId> <version>0.0.1-SNAPSHOT</version> <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> <parent> <artifactId>Oauth</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.authzserver</artifactId> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.0</version> </dependency> </dependencies> </project> 

在wx-server 的项目中

依赖导入完成后就生成代码。oath_user,oath_company

不知道如何生成代码可以看我博客中写的 : 

【OAuth2】用户授权第三方应用,使用授权码模式实例案例进行详解(源代码)  其 搭建使用 中的 生成。

在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:  url: jdbc:mysql://localhost:3306/oauth?serverTimezone=GMT redis: host: localhost port: 6379 database: 0 server: port: 9999

在 zking-server 的项目中,生成根据数据表中的 zking_user 表

其中项目的pom.xml依赖有以下 : 

<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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> <groupId>com.example</groupId> <artifactId>zking-server</artifactId> <version>0.0.1-SNAPSHOT</version> <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> <parent> <artifactId>Oauth</artifactId> <groupId>org.example</groupId> <version>1.0-SNAPSHOT</version> </parent> <dependencies> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.client</artifactId> </dependency> </dependencies> </project> 

在 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:  url: jdbc:mysql://localhost:3306/oauth?serverTimezone=GMT server: port: 8080 

在 zking-server 的项目中的resources文件中创建templates文件,再分别创建login.ftl

phone.ftl

login.ftl

<!doctype html>
<html lang="zh">
<head>
    <title>Document</title>
</head>
<body>
<form action="${springMacroRequestContext.contextPath}/client/login">
    <p>账户:<input type="text" name="account"></p>
    <p>密码:<input type="password" name="password"></p>
    <p>
        <button>登录</button>
        <a href="${springMacroRequestContext.contextPath}/client/wx">微信登录</a>
    </p>
</form>
</body>
</html>

phone.ftl

<!doctype html>
<html lang="zh">
<head>
    <title>Document</title>
</head>
<body>
<form action="">
    <input name="nickName">
    <button>绑定</button>
</form>
</body>
</html>

在 wx-server 的项目中的resources文件中创建templates文件,再分别创建login.ftl

error.ftl及auth.ftl

login.ftl

<!doctype html>
<html lang="zh">
<head>
    <title>Document</title>
</head>
<body>
<form action="${springMacroRequestContext.contextPath}/auth/doLogin" method="post">
    <p>账户:<input type="text" name="account"></p>
    <p>密码:<input type="password" name="password"></p>
    <p>回调地址<input type="text" name="redirect_uri" value="${redirect_uri}" readonly></p>
    <p>申请公司:${company_info.clientName}</p>
    <p>申请公司标识:${company_info.clientId}</p>
    <p>
        <button>登录</button>
    </p>
</form>
</body>
</html>

error.ftl

<!doctype html>
<html lang="zh">
<head>
    <title>Document</title>
</head>
<body>
<h1>请检查贵公司是否与我们有合作</h1>
<h1>也可能你拒绝了授权</h1>
</body>
</html>

auth.ftl

<!doctype html>
<html lang="zh">
<head>
    <title>Document</title>
</head>
<body>
<form action="${springMacroRequestContext.contextPath}/auth/doAuth">
    <p><input type="text" name="snowId" value="${snowId}" readonly></p>
    <button>同意授权</button>
</form>
</body>
</html>

4. 授权处理

在 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<String, Object> 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<String, Object> 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<Company>().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<User>().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: 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<Company>().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<User>().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); //获取令牌 371token 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<ZkingUser>().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的标准规范,提高了项目的可维护性和可扩展性。

总之,使用授权码模式可以提高项目的安全性和用户体验,同时也可以使得项目符合标准规范,具有更好的灵活性和通用性。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/136695.html

(0)
上一篇 2025-06-25 18:15
下一篇 2025-06-25 18:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信