大家好,欢迎来到IT知识分享网。
目录
一、引言
1. 详解
授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。
这种方式是最常用的流程,安全性也最高,它适用于那些有后端的 Web 应用。授权码通过前端传送,令牌则是储存在后端,而且所有与资源服务器的通信都在后端完成。这样的前后端分离,可以避免令牌泄漏。
适用场景 :
1. Web应用程序:当客户端是一个运行在Web服务器上的应用程序时,授权码模式是一个常见的选择。用户在Web应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。
2. 移动应用程序:对于需要访问受保护资源的移动应用程序,授权码模式也是一个很好的选择。用户可以在移动应用程序中进行登录和授权,然后客户端使用授权码来获取访问令牌。
3. 第三方应用程序:当一个第三方应用程序需要代表用户访问受保护资源时,授权码模式也可以使用。用户可以在授权服务器上进行登录和授权,然后第三方应用程序使用授权码来获取访问令牌。
总之,授权码模式适用于需要在用户和客户端之间建立信任关系,并且需要通过授权码来获取访问令牌的场景。它是一种相对安全的授权方式,适用于多种不同类型的应用程序。
2. 实现流程
主要流程 :
- 用户访问客户端,客户端将用户重定向到授权服务器。
- 用户在授权服务器上登录并同意授权客户端请求的权限。
- 授权服务器将用户重定向回客户端,并附上一个授权码。
- 客户端收到授权码后,使用该授权码向授权服务器请求访问令牌。
- 客户端向授权服务器发送包含授权码的请求,并提供客户端凭证(客户端ID和客户端密钥)。
- 授权服务器验证客户端凭证和授权码,如果验证通过,授权服务器将颁发访问令牌给客户端。
- 客户端使用访问令牌向受保护资源服务器请求受保护资源。
通过以上流程,客户端可以通过授权码模式获取访问令牌,并使用该访问令牌访问受保护资源。这种方式可以保护用户的敏感信息,同时也可以保护客户端的凭证信息。
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. 数据表
首先需要创建数据表,根据以下表格及字段创建即可,或有更好的也可
2. 项目结构
如何我们使用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