上一节中我们已经处理完了
上一节中我们已经走完了oauth流程,拿到了服务提供商的信息,但是在交给OAuthenticationProvider处理时候出了问题,在SocialAuthenticationProvider处他把我们导向了signup的路径,下面我们看下为什么出现在这个结果。
SocialAuthenticationProvider类下的authenticate方法:
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//1.判断是否是我们之前通过Connection组装的SocialAuthenticationToken授权对象
Assert.isInstanceOf(SocialAuthenticationToken.class, authentication, "unsupported authentication type");
Assert.isTrue(!authentication.isAuthenticated(), "already authenticated");
SocialAuthenticationToken authToken = (SocialAuthenticationToken)authentication;
String providerId = authToken.getProviderId();
//2.connection里面封装了:qq的providerId(qq应用id)和providerUserId(qq应用上的用户id)
Connection<?> connection = authToken.getConnection();
//3.根据connection里的roviderId和providerUserId去用户关联表查询对应业务系统的userId
String userId = this.toUserId(connection);
//4.第一次获取不到,会跑一个异常,异常最后会被:Social AuthenticationFilter获取到
if (userId == null) {
throw new BadCredentialsException("Unknown access token");
} else {
UserDetails userDetails = this.userDetailsService.loadUserByUserId(userId);
if (userDetails == null) {
throw new UsernameNotFoundException("Unknown connected account id");
} else {
return new SocialAuthenticationToken(connection, userDetails, authToken.getProviderAccountData(), this.getAuthorities(providerId, userDetails));
}
}
}
protected String toUserId(Connection<?> connection) {
List<String> userIds = this.usersConnectionRepository.findUserIdsWithConnection(connection);
return userIds.size() == 1 ? (String)userIds.iterator().next() : null;
}
throw new BadCredentialsException("Unknown access token")抛去异常会被SocialAuthenticationFilter捕获到:如果你配置了一个signupUrl,那么就会跳转到signupUrl地址。
默认的signupUrl是"/signup";
因为我们没有对:signup做授权,所以会被拦截下来,然后跳转到需要授权的请求路径上:/authentication/require
我们该怎样解决这个问题呢?其实很简单:我们自己写一个注册页,然后把signupUrl配置成我们自己写的注册页,最后再配置下授权,让那个注册页不经过身份认证就可以访问。
针对于不同的平台注册页信息不同,所以我们需要自定义注册页信息,这个信息是写在S配置文件中:具体注册页是在spring-security-demo里面自己写的。我们只会在spring-security-web时候做一个提示。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>注册</title>
</head>
<body>
<h2>标准注册页面</h2>
<h3>这是系统注册页面,请配置yxm.security.browser.signUpUrl属性来设置自己的注册页</h3>
</body>
</html>
有了上面配置项之后我们就可以在spring-security-demo里面去配置:
在spring-security-demo里面,我们配置个性化的登录页:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h2>Demo注册页</h2>
<form action="/user/regist" method="post">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2">
<!--不存在的用户:我们是采用注册:regist-->
<button type="submit" name="type" value="regist">注册</button>
<!--已经存在的用户,我们是去做一个绑定-->
<button type="submit" name="type" value="binding">绑定</button>
</td>
</tr>
</table>
</form>
</body>
</html>
我们在UserController里面注册用户
我们授权signUp的请求,让系统不去拦截。
让过滤器知道,找不到用户的时候,跳到我们指定的地址上去:
以上我们测试: 1.点击qq登录会转到需要在第三方授权的页面,然后授权之后会返回到第三方应用系统自带的登录页
为了处理这两个问题,Spring提供了一个工具类ProviderSignInUtils.我们在SocialConfig中对其进行配置,其实他就解决了2个问题:
然后我们在WebSecurityController里面提供一个使用ProviderSignInUtils返回SocialUserInfo的方法
有了这些SocialUserInfo前台就能返回一个友好的用户提示来。
public class SocialUserInfo {
private String providerId;
private String providerUserId;
private String nickname;
private String headimg;
//getter setter方法
}
我们从session里面获取到了对应的值,那么我们是什么时候放入到session里面的呢?
我们先把SocialUserInfo部门相关信息存储在session中,然后跳转到signUp,然后进入/social/user类中来设置用户信息。
我们将信息传给user后,到数据库中注册到User表,再用providerSignUitls到数据库中注册到Userconnetion表,因为需要从session中取值,所以还需呀传一个request.这里为了方便没写具体的注册逻辑.
这个路径按理说是demo项目里的路径,只有demo项目知道它要到这里面注册,在后面我们在授权的时候将只有用户知道的路径剥离,让用户自己去写.
输入QQ凭证,进入注册页,进行注册,userId为xxx,再次输入QQ凭证,可以直接进入系统,打印出的凭证显示userId为xxx.