上一节中我们已经处理完了
上一节中我们已经走完了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.