概述
这个问题拦住我两天,搞了一个周六没搞出来,o(╥﹏╥)o
一、前提
已经使用SpringCloud oauth2 + Security 搭建好了完整的网关 + 认证服务 + 普通的业务的服务
二、现象
通过网关请求授权码访问认证服务,请求授权码
localhost:8002/api/uaa/oauth/authorize?response_type=code&client_id=c1&redirect_uri=http://www.baidu.com&scope=ROLE_ADMIN
发现跳转到到认证服务的登录页面【注意浏览器的地址栏,ip和端口变成了认证服务的了!!!】
输入账号密码登录后,并没有跳转到到授权页面,而是跳转到认证服务的根目录下
三、原因
在这里找到原因: https://ask.csdn.net/questions/1061712
总结一下,就是SpringSecurity会缓存登录前的上一次请求在session中,在登录成功后,跳往该请求。由于第一次请求是 localhost:8002/api/uaa/oauth/authorize,到登录时变成了 http://192.168.56.1:8003/login,大家都知道session是根据cookie中的jsessonid来寻找的,由于ip和端口变了,http://192.168.56.1:8003 自然不会携带localhost:8002的cookie,所以登录后的session就不是 localhost:8002/api/uaa/oauth/authorize 当时缓存上一次请求的session
四、解决:
在网关层添加 过滤器,当重定向到认证服务的登录页时,将认证服务的域名和端口替换成网关的域名和端口
在
1、网关添加过滤器
@Component
public class ResponseGlobalFilter implements GlobalFilter , Ordered {
private String crossOriginPath = "http://localhost:8002";
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String path = exchange.getRequest().getPath().value();
if (path.contains("/oauth/authorize") ) {
//构建响应包装类
HttpResponseDecorator responseDecorator = new HttpResponseDecorator(exchange.getRequest(), exchange.getResponse(), crossOriginPath);
return chain
.filter(exchange.mutate().response(responseDecorator).build());
}
return chain.filter(exchange);
}
}
public class HttpResponseDecorator extends ServerHttpResponseDecorator {
private String proxyUrl;
private ServerHttpRequest request;
/**
* 构造函数
*
* @param delegate
*/
public HttpResponseDecorator(ServerHttpRequest request, ServerHttpResponse delegate, String proxyUrl) {
super(delegate);
this.request = request;
this.proxyUrl = proxyUrl;
}
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
HttpStatus status = this.getStatusCode();
if (status.equals(HttpStatus.FOUND)) {
String domain = domain = proxyUrl + "/api/uaa";
String location = getHeaders().getFirst("Location");
String replaceLocation = location.replaceAll("^((ht|f)tps?):\/\/(\d{1,3}.){3}\d{1,3}(:\d+)?", domain);
if (location.contains("code=")) {
} else {
getHeaders().set("Location", replaceLocation);
}
}
this.getStatusCode();
return super.writeWith(body);
}
}
2、重启服务后访问授权码
可以看到确实通过网关跳转的登录页
输入账户密码登录
1)猜测问题 (自己瞎琢磨的,大家就当看个笑话)
http://localhost:8002/login 这个接口肯定是输入账号密码的认证接口了,SpringSecurity中也是这么配置的,默认表单的提交路径很可能是 ,所以直接就用当前的域名 + 端口 + login (http://localhost:8002/login) 来请求认证了
3、知道问题后,想办法解决问题
希望将默认表单的 提交路径改为
想了一下,好像没办法改默认表单的提交路径,那么只有重写表单页了
1)uaa服务resource下,新建statc文件夹,新建MyLogin.html 表单登录页
2) MyLogin.html 表达提交路径改为 /api/uaa/login
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div class="login-container">
<div class="form-container">
<form class="form-signin" method="post" action="/api/uaa/login">
<h2 class="form-signin-heading">用户登录</h2>
<p>
<label for="username" class="sr-only">用户名</label>
<input type="text" id="username" name="username" class="form-control" placeholder="用户名" required
autofocus>
</p>
<p>
<label for="password" class="sr-only">密码</label>
<input type="password" id="password" name="password" class="form-control" placeholder="密码" required>
</p>
<button class="btn btn-lg btn-primary btn-block" type="submit">登 录</button>
</form>
</div>
</div>
</body>
<style>
.login-container {
margin: 50px;
width: 100%;
}
.form-container {
margin: 0px auto;
width: 50%;
text-align: center;
box-shadow: 1px 1px 10px #888888;
height: 300px;
padding: 5px;
}
input {
margin-top: 10px;
width: 350px;
height: 30px;
border-radius: 3px;
border: 1px #E9686B solid;
padding-left: 2px;
}
.btn {
width: 350px;
height: 35px;
line-height: 35px;
cursor: pointer;
margin-top: 20px;
border-radius: 3px;
background-color: #E9686B;
color: white;
border: none;
font-size: 15px;
}
.title {
margin-top: 5px;
font-size: 18px;
color: #E9686B;
}
</style>
</html>
3)及得SpringSecurity的配置 WebSecurityConfig 配置 loginPage和 loginProcessingUrl
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().formLogin()
.loginPage("MyLogin.html")
.loginProcessingUrl("/login").permitAll()
// .successHandler(successHandler).permitAll()
.failureHandler(failureHandler).permitAll().and()
.logout().logoutSuccessHandler(logoutHandler).and()
.authorizeRequests()
.antMatchers("/security/**").permitAll();
}
4) 测试
确实跳转到登录页
点击登录后,跳转到授权页面
点击授权后,看来和上面是一个问题,需要将默认授权页面的提交接口从 /oauth/authorize 改成
/api/uaa/oauth/authorize
5) 自定义授权页面
a、uaa引入 thymeleaf
<!-- thymeleaf 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
b、配置文件添加thymeleaf配置
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
c、在resources目录下面建立templates目录,新建 grant.html
【注意】form提交路径要改为 /api/uaa/oauth/authorize
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>授权</title>
</head>
<body style="margin: 0px">
<div class="title">
<div class="title-right">oauth2授权</div>
<div class="title-left">
<a href="#help">帮助</a>
</div>
</div>
<div class="container">
<h3 th:text="${clientId}+' 请求授权,该应用将获取你的以下信息'"></h3>
<p>昵称,头像和性别</p>
授权后表明你已同意 <a href="#boot" style="color: #E9686B">服务协议</a>
<form method="post" action="/api/uaa/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>
<style>
html{
padding: 0px;
margin: 0px;
}
.title {
background-color: #E9686B;
height: 50px;
padding-left: 20%;
padding-right: 20%;
color: white;
line-height: 50px;
font-size: 18px;
}
.title-left{
float: right;
}
.title-right{
float: left;
}
.title-left a{
color: white;
}
.container{
clear: both;
text-align: center;
}
.btn {
width: 350px;
height: 35px;
line-height: 35px;
cursor: pointer;
margin-top: 20px;
border-radius: 3px;
background-color: #E9686B;
color: white;
border: none;
font-size: 15px;
}
</style>
</body>
</html>
d、新写controller覆盖oauth2 的 confirm_access接口
package com.uaa.controller;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.oauth2.provider.AuthorizationRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
@Controller
//必须配置
@SessionAttributes("authorizationRequest")
public class BootGrantController {
@RequestMapping("/oauth/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("grant"); //自定义页面名字,resourcestemplatesbase-grant.html
view.addObject("clientId", authorizationRequest.getClientId());
view.addObject("scopes",authorizationRequest.getScope());
return view;
}
}
e、测试
点击同意后,顺利拿到回调的code
五、总结
虽然解决了问题,但是感觉自己写的东西太多了,待会再研究下有没有更好的解决方案吧!!!
不开心 o(╥﹏╥)o
最后
以上就是耍酷烧鹅为你收集整理的oauth2 通过gateway请求授权码不能回调到return_uri的全部内容,希望文章能够帮你解决oauth2 通过gateway请求授权码不能回调到return_uri所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复