由于 cookie 冲突(授权码机制),官方 Spring 安全性 oauth2 示例不起作用
Official Spring security oauth2 example doesn't work because of cookies clashing(authorization code mechanism)
我有以下项目结构:
以及以下源代码:
SocialApplication.class:
@SpringBootApplication
@RestController
@EnableOAuth2Client
@EnableAuthorizationServer
@Order(200)
public class SocialApplication extends WebSecurityConfigurerAdapter {
@Autowired
OAuth2ClientContext oauth2ClientContext;
@RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
.logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// @formatter:on
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// @formatter:on
}
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean
@ConfigurationProperties("github")
public ClientResources github() {
return new ClientResources();
}
@Bean
@ConfigurationProperties("facebook")
public ClientResources facebook() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(facebook(), "/login/facebook"));
filters.add(ssoFilter(github(), "/login/github"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(new UserInfoTokenServices(
client.getResource().getUserInfoUri(),
client.getClient().getClientId()));
return filter;
}
}
class ClientResources {
@NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
@NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Demo</title>
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width"/>
<base href="/"/>
<link rel="stylesheet" type="text/css"
href="/webjars/bootstrap/css/bootstrap.min.css"/>
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript"
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<h1>Login</h1>
<div class="container unauthenticated">
With Facebook: <a href="/login/facebook">click here</a>
</div>
<div class="container authenticated" style="display: none">
Logged in as: <span id="user"></span>
<div>
<button onClick="logout()" class="btn btn-primary">Logout</button>
</div>
</div>
<script type="text/javascript"
src="/webjars/js-cookie/js.cookie.js"></script>
<script type="text/javascript">
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (settings.type == 'POST' || settings.type == 'PUT'
|| settings.type == 'DELETE') {
if (!(/^http:.*/.test(settings.url) || /^https:.*/
.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-XSRF-TOKEN",
Cookies.get('XSRF-TOKEN'));
}
}
}
});
$.get("/user", function (data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
var logout = function () {
$.post("/logout", function () {
$("#user").html('');
$(".unauthenticated").show();
$(".authenticated").hide();
});
return true;
}
</script>
</body>
</html>
application.yml:
server:
port: 8080
security:
oauth2:
client:
client-id: acme
client-secret: acmesecret
scope: read,write
auto-approve-scopes: '.*'
facebook:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
github:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
logging:
level:
org.springframework.security: DEBUG
但是当我打开浏览器并尝试点击 http://localhost:8080
在浏览器控制台中我看到:
(index):44 Uncaught TypeError: Cannot read property 'details' of undefined
at Object.success ((index):44)
at j (jquery.js:3073)
at Object.fireWith [as resolveWith] (jquery.js:3185)
at x (jquery.js:8251)
at XMLHttpRequest.<anonymous> (jquery.js:8598)
在代码中:
$.get("/user", function (data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
这是因为 /user
响应 302 状态码和 js 回调试图解析 localhost:8080
:
的结果
我不明白为什么会发生这种重定向。你能解释一下这种行为并帮助解决它吗?
更新
我从 https://github.com/spring-guides/tut-spring-boot-oauth2
中获取了这段代码
重要:
它仅在我启动客户端应用程序后才重现。
P.S.
重现方式:
To test the new features you can just run both apps and visit
localhost:9999/client in your browser. The client app will redirect to
the local Authorization Server, which then gives the user the usual
choice of authentication with Facebook or Github. Once that is
complete control returns to the test client, the local access token is
granted and authentication is complete (you should see a "Hello"
message in your browser). If you are already authenticated with Github
or Facebook you may not even notice the remote authentication
答案:
更新:2018 年 5 月 15 日
正如您已经找到解决方案一样,问题的发生是因为 JSESSIONID
被覆盖了
更新:2018 年 5 月 10 日
好吧,您对第 3 个赏金的坚持终于得到了回报。我开始深入研究你在 repo
中的两个例子之间的不同之处
如果您查看 manual
存储库和 /user
映射
@RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
如您所见,您在这里返回了 principal
,您从同一对象中获得了更多详细信息。现在在你的代码中你 运行 from auth-server
folder
@RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
如您所见,您只返回了 /user
映射中的 name
和下面 运行 的 UI 逻辑
$.get("/user", function(data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
因此 json 从 /user
api 返回的 UI 预期具有 userAuthentication.details.name
的响应没有该详细信息。现在,如果我在同一个项目中更新如下方法
@RequestMapping({"/user", "/me"})
public Map<String, Object> user(Principal principal) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("name", principal.getName());
OAuth2Authentication user = (OAuth2Authentication) principal;
map.put("userAuthentication", new HashMap<String, Object>(){{
put("details", user.getUserAuthentication().getDetails());
}});
return map;
}
然后检查应用程序,它有效
原答案
所以问题是您 运行 从存储库中选择了错误的项目。您正在 运行ning 的项目是 auth-server
,用于启动您自己的 oauth
服务器。您需要 运行 的项目在 manual
文件夹中。
现在,如果你看下面的代码
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(),
facebook().getClientId());
tokenServices.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(
new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
return facebookFilter;
而您 运行 拥有的实际代码
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
return filter;
}
在您当前的 facebook
中,userdetails
未被收集。这就是您看到错误的原因
因为当您登录用户时,您没有收集其用户详细信息。因此,当您访问详细信息时,它不在那里。因此你得到一个错误
如果您 运行 正确的 manual
文件夹,它就可以工作
我在您的 post 中看到两个查询。
ONE-
(index):44 Uncaught TypeError: Cannot read property 'details' of undefined
发生这种情况是因为您可能 运行 正在使用一个错误的项目(即 auth-server),它有一个错误。该回购协议包含其他类似项目也没有错误。如果你运行项目manual或者github这个错误就不会出现。在这些项目中,javascript 代码正确处理了身份验证后由服务器 return 编辑的数据。
两个-
/user
响应 302 状态码:
要了解发生这种情况的原因,请查看此应用程序的安全配置。
终点 "/"
、"/login**"
和 "/logout"
对所有人开放。
包括 "/user"
在内的所有其他端点都需要身份验证,因为您已使用
.anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
因此,任何未经身份验证的请求都将被重定向到身份验证入口点,即 "/"
,要求用户进行身份验证。它不取决于您的客户端应用程序是否已启动。只要请求未通过身份验证,它就会被重定向到 "/"
。这就是 spring 控制器以状态 302 响应的原因。一旦您使用 facebook 或 github 进行身份验证,对 "/user"
端点的后续请求将以 200 响应成功。
和下一个-
您的应用程序中的端点 "/me"
作为安全资源受到 @EnableResourceServer
的保护。由于 ResourceServerConfiguration
的优先级(默认为 3)高于 WebSecurityConfigurerAdapter
(默认为 100,无论如何它已经在代码中使用 @Order 注释明确排序低于 3),因此 ResourceServerConfiguration 将应用于此端点。这意味着如果请求未通过身份验证,那么它 将不会 重定向到身份验证入口点,它宁愿 return 响应 401 .一旦您通过身份验证,它将以 200 响应成功。
希望这会澄清你所有的问题。
更新- 来回答你的问题
您在 post 中提供的存储库 link 包含许多项目。 auth-server、manual 和 github 项目都很相似(提供相同的功能,即使用 facebook 和 github 进行身份验证)。只是 auth-server projet 中的 index.html
有一个错误。如果您更正此错误,即 replace
$("#user").html(data.userAuthentication.details.name);
和
$("#user").html(data.name);
它也 运行 没问题。所有三个项目都将提供相同的输出。
终于找到问题所在了。如果您在本地主机上启动两个应用程序,客户端和服务器的 cookie 会发生冲突,我会看到此行为。
它的发生是由于上下文使用错误属性。
所以要修复应用程序,您需要替换:
server:
context-path: /client
和
server:
servlet:
context-path: /client
P.S.
我在 github 上创建了问题:
https://github.com/spring-guides/tut-spring-boot-oauth2/issues/80
并提出拉取请求:
https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81
P.S.2
终于合并了我的拉取请求:
https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81
我有以下项目结构:
以及以下源代码:
SocialApplication.class:
@SpringBootApplication
@RestController
@EnableOAuth2Client
@EnableAuthorizationServer
@Order(200)
public class SocialApplication extends WebSecurityConfigurerAdapter {
@Autowired
OAuth2ClientContext oauth2ClientContext;
@RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**").permitAll().anyRequest()
.authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
.logoutSuccessUrl("/").permitAll().and().csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
// @formatter:on
}
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
// @formatter:on
}
}
public static void main(String[] args) {
SpringApplication.run(SocialApplication.class, args);
}
@Bean
public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(OAuth2ClientContextFilter filter) {
FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<OAuth2ClientContextFilter>();
registration.setFilter(filter);
registration.setOrder(-100);
return registration;
}
@Bean
@ConfigurationProperties("github")
public ClientResources github() {
return new ClientResources();
}
@Bean
@ConfigurationProperties("facebook")
public ClientResources facebook() {
return new ClientResources();
}
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
filters.add(ssoFilter(facebook(), "/login/facebook"));
filters.add(ssoFilter(github(), "/login/github"));
filter.setFilters(filters);
return filter;
}
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(),
client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(new UserInfoTokenServices(
client.getResource().getUserInfoUri(),
client.getClient().getClientId()));
return filter;
}
}
class ClientResources {
@NestedConfigurationProperty
private AuthorizationCodeResourceDetails client = new AuthorizationCodeResourceDetails();
@NestedConfigurationProperty
private ResourceServerProperties resource = new ResourceServerProperties();
public AuthorizationCodeResourceDetails getClient() {
return client;
}
public ResourceServerProperties getResource() {
return resource;
}
}
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<title>Demo</title>
<meta name="description" content=""/>
<meta name="viewport" content="width=device-width"/>
<base href="/"/>
<link rel="stylesheet" type="text/css"
href="/webjars/bootstrap/css/bootstrap.min.css"/>
<script type="text/javascript" src="/webjars/jquery/jquery.min.js"></script>
<script type="text/javascript"
src="/webjars/bootstrap/js/bootstrap.min.js"></script>
</head>
<body>
<h1>Login</h1>
<div class="container unauthenticated">
With Facebook: <a href="/login/facebook">click here</a>
</div>
<div class="container authenticated" style="display: none">
Logged in as: <span id="user"></span>
<div>
<button onClick="logout()" class="btn btn-primary">Logout</button>
</div>
</div>
<script type="text/javascript"
src="/webjars/js-cookie/js.cookie.js"></script>
<script type="text/javascript">
$.ajaxSetup({
beforeSend: function (xhr, settings) {
if (settings.type == 'POST' || settings.type == 'PUT'
|| settings.type == 'DELETE') {
if (!(/^http:.*/.test(settings.url) || /^https:.*/
.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-XSRF-TOKEN",
Cookies.get('XSRF-TOKEN'));
}
}
}
});
$.get("/user", function (data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
var logout = function () {
$.post("/logout", function () {
$("#user").html('');
$(".unauthenticated").show();
$(".authenticated").hide();
});
return true;
}
</script>
</body>
</html>
application.yml:
server:
port: 8080
security:
oauth2:
client:
client-id: acme
client-secret: acmesecret
scope: read,write
auto-approve-scopes: '.*'
facebook:
client:
clientId: 233668646673605
clientSecret: 33b17e044ee6a4fa383f46ec6e28ea1d
accessTokenUri: https://graph.facebook.com/oauth/access_token
userAuthorizationUri: https://www.facebook.com/dialog/oauth
tokenName: oauth_token
authenticationScheme: query
clientAuthenticationScheme: form
resource:
userInfoUri: https://graph.facebook.com/me
github:
client:
clientId: bd1c0a783ccdd1c9b9e4
clientSecret: 1a9030fbca47a5b2c28e92f19050bb77824b5ad1
accessTokenUri: https://github.com/login/oauth/access_token
userAuthorizationUri: https://github.com/login/oauth/authorize
clientAuthenticationScheme: form
resource:
userInfoUri: https://api.github.com/user
logging:
level:
org.springframework.security: DEBUG
但是当我打开浏览器并尝试点击 http://localhost:8080
在浏览器控制台中我看到:
(index):44 Uncaught TypeError: Cannot read property 'details' of undefined
at Object.success ((index):44)
at j (jquery.js:3073)
at Object.fireWith [as resolveWith] (jquery.js:3185)
at x (jquery.js:8251)
at XMLHttpRequest.<anonymous> (jquery.js:8598)
在代码中:
$.get("/user", function (data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
这是因为 /user
响应 302 状态码和 js 回调试图解析 localhost:8080
:
我不明白为什么会发生这种重定向。你能解释一下这种行为并帮助解决它吗?
更新
我从 https://github.com/spring-guides/tut-spring-boot-oauth2
中获取了这段代码重要:
它仅在我启动客户端应用程序后才重现。
P.S.
重现方式:
To test the new features you can just run both apps and visit localhost:9999/client in your browser. The client app will redirect to the local Authorization Server, which then gives the user the usual choice of authentication with Facebook or Github. Once that is complete control returns to the test client, the local access token is granted and authentication is complete (you should see a "Hello" message in your browser). If you are already authenticated with Github or Facebook you may not even notice the remote authentication
答案:
更新:2018 年 5 月 15 日
正如您已经找到解决方案一样,问题的发生是因为 JSESSIONID
被覆盖了
更新:2018 年 5 月 10 日
好吧,您对第 3 个赏金的坚持终于得到了回报。我开始深入研究你在 repo
中的两个例子之间的不同之处如果您查看 manual
存储库和 /user
映射
@RequestMapping("/user")
public Principal user(Principal principal) {
return principal;
}
如您所见,您在这里返回了 principal
,您从同一对象中获得了更多详细信息。现在在你的代码中你 运行 from auth-server
folder
@RequestMapping({ "/user", "/me" })
public Map<String, String> user(Principal principal) {
Map<String, String> map = new LinkedHashMap<>();
map.put("name", principal.getName());
return map;
}
如您所见,您只返回了 /user
映射中的 name
和下面 运行 的 UI 逻辑
$.get("/user", function(data) {
$("#user").html(data.userAuthentication.details.name);
$(".unauthenticated").hide();
$(".authenticated").show();
});
因此 json 从 /user
api 返回的 UI 预期具有 userAuthentication.details.name
的响应没有该详细信息。现在,如果我在同一个项目中更新如下方法
@RequestMapping({"/user", "/me"})
public Map<String, Object> user(Principal principal) {
Map<String, Object> map = new LinkedHashMap<>();
map.put("name", principal.getName());
OAuth2Authentication user = (OAuth2Authentication) principal;
map.put("userAuthentication", new HashMap<String, Object>(){{
put("details", user.getUserAuthentication().getDetails());
}});
return map;
}
然后检查应用程序,它有效
原答案
所以问题是您 运行 从存储库中选择了错误的项目。您正在 运行ning 的项目是 auth-server
,用于启动您自己的 oauth
服务器。您需要 运行 的项目在 manual
文件夹中。
现在,如果你看下面的代码
OAuth2ClientAuthenticationProcessingFilter facebookFilter = new OAuth2ClientAuthenticationProcessingFilter(
"/login/facebook");
OAuth2RestTemplate facebookTemplate = new OAuth2RestTemplate(facebook(), oauth2ClientContext);
facebookFilter.setRestTemplate(facebookTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(facebookResource().getUserInfoUri(),
facebook().getClientId());
tokenServices.setRestTemplate(facebookTemplate);
facebookFilter.setTokenServices(
new UserInfoTokenServices(facebookResource().getUserInfoUri(), facebook().getClientId()));
return facebookFilter;
而您 运行 拥有的实际代码
private Filter ssoFilter(ClientResources client, String path) {
OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
path);
OAuth2RestTemplate template = new OAuth2RestTemplate(client.getClient(), oauth2ClientContext);
filter.setRestTemplate(template);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(
client.getResource().getUserInfoUri(), client.getClient().getClientId());
tokenServices.setRestTemplate(template);
filter.setTokenServices(tokenServices);
return filter;
}
在您当前的 facebook
中,userdetails
未被收集。这就是您看到错误的原因
因为当您登录用户时,您没有收集其用户详细信息。因此,当您访问详细信息时,它不在那里。因此你得到一个错误
如果您 运行 正确的 manual
文件夹,它就可以工作
我在您的 post 中看到两个查询。
ONE-
(index):44 Uncaught TypeError: Cannot read property 'details' of undefined
发生这种情况是因为您可能 运行 正在使用一个错误的项目(即 auth-server),它有一个错误。该回购协议包含其他类似项目也没有错误。如果你运行项目manual或者github这个错误就不会出现。在这些项目中,javascript 代码正确处理了身份验证后由服务器 return 编辑的数据。
两个-
/user
响应 302 状态码:
要了解发生这种情况的原因,请查看此应用程序的安全配置。
终点 "/"
、"/login**"
和 "/logout"
对所有人开放。
包括 "/user"
在内的所有其他端点都需要身份验证,因为您已使用
.anyRequest().authenticated().and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
因此,任何未经身份验证的请求都将被重定向到身份验证入口点,即 "/"
,要求用户进行身份验证。它不取决于您的客户端应用程序是否已启动。只要请求未通过身份验证,它就会被重定向到 "/"
。这就是 spring 控制器以状态 302 响应的原因。一旦您使用 facebook 或 github 进行身份验证,对 "/user"
端点的后续请求将以 200 响应成功。
和下一个-
您的应用程序中的端点 "/me"
作为安全资源受到 @EnableResourceServer
的保护。由于 ResourceServerConfiguration
的优先级(默认为 3)高于 WebSecurityConfigurerAdapter
(默认为 100,无论如何它已经在代码中使用 @Order 注释明确排序低于 3),因此 ResourceServerConfiguration 将应用于此端点。这意味着如果请求未通过身份验证,那么它 将不会 重定向到身份验证入口点,它宁愿 return 响应 401 .一旦您通过身份验证,它将以 200 响应成功。
希望这会澄清你所有的问题。
更新- 来回答你的问题
您在 post 中提供的存储库 link 包含许多项目。 auth-server、manual 和 github 项目都很相似(提供相同的功能,即使用 facebook 和 github 进行身份验证)。只是 auth-server projet 中的 index.html
有一个错误。如果您更正此错误,即 replace
$("#user").html(data.userAuthentication.details.name);
和
$("#user").html(data.name);
它也 运行 没问题。所有三个项目都将提供相同的输出。
终于找到问题所在了。如果您在本地主机上启动两个应用程序,客户端和服务器的 cookie 会发生冲突,我会看到此行为。
它的发生是由于上下文使用错误属性。
所以要修复应用程序,您需要替换:
server:
context-path: /client
和
server:
servlet:
context-path: /client
P.S.
我在 github 上创建了问题:
https://github.com/spring-guides/tut-spring-boot-oauth2/issues/80
并提出拉取请求:
https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81
P.S.2
终于合并了我的拉取请求: https://github.com/spring-guides/tut-spring-boot-oauth2/pull/81