发布网友 发布时间:2024-10-04 06:46
共1个回答
热心网友 时间:2024-10-27 16:40
一、目标前一章节讲解了各应用未登录的系统统一跳转至SSO统一登录页面。当输入用户名、密码后点击登录流程是怎么实现的。这一章节的目标主要是讲解OA(这里代表client.sso.com:8082)经过统一登录后怎么回跳至OA页面。
二 、系统UML工程类图三、代码实现a. com.yuantai.controller.LoginController@RequestMapping(method?=?RequestMethod.POST)????public?String?login(????????????@RequestParam(value?=?SsoConstant.REDIRECT_URI,?required?=?true)?String?redirectUri,????????????@RequestParam(value?=?Oauth2Constant.APP_ID,?required?=?true)?String?appId,????????????@RequestParam?String?username,????????????@RequestParam?String?password,????????????HttpServletRequest?request,?HttpServletResponse?response)?throws?UnsupportedEncodingException?{????????if(!appService.exists(appId))?{????????????request.setAttribute("errorMessage",?"非法应用");????????????return?goLoginPath(redirectUri,?appId,?request);????????}????????Result<SsoUser>?result?=?userService.login(username,?password);????????if?(!result.isSuccess())?{????????????request.setAttribute("errorMessage",?result.getMessage());????????????return?goLoginPath(redirectUri,?appId,?request);????????}????????String?tgt?=?sessionManager.setUser(result.getData(),?request,?response);????????return?generateCodeAndRedirect(redirectUri,?tgt);????}1、 输入(用户名、密码)点击登录,首次调用的是/login方法(SmartSsoConfig配置了无需拦截此url) 2、调用com.yuantai.session.TicketGrantingTicketManager的 sessionManager.setUser()
public?String?setUser(SsoUser?user,?HttpServletRequest?request,?HttpServletResponse?response)?{????????String?tgt?=?getCookieTgt(request);????????if?(StringUtils.isEmpty(tgt))?{????????????//?cookie中没有?生成一个tgt????????????tgt?=?ticketGrantingTicketManager.generate(user);????????????//?TGT存cookie,和Cas登录保存cookie中名称一致为:TGC????????????CookieUtils.addCookie(AppConstant.TGC,?tgt,?"/",?request,?response);????????}????????else?if(ticketGrantingTicketManager.getAndRefresh(tgt)?==?null){????????????ticketGrantingTicketManager.create(tgt,?user);????????}????????else?{????????????ticketGrantingTicketManager.set(tgt,?user);????????}????????return?tgt;????}当进入if(StringUtils.isEmpty(tgt))判断时getCookieTgt(request)获取的tgt为空(/login登录只传了用户名与密码并无其他信息)所以此判断为true进去方法体代码中、此方法体作用如下:
生成一个管理端的登录凭证TGT
把凭证与用户信息存储内存Map(TGT,用户信息)
把凭证等信息放入Cookie中
3、调用com.yuantai.controller.BaseLoginController的generateCodeAndRedirect(redirectUri, tgt)方法
String?generateCodeAndRedirect(String?redirectUri,?String?tgt)?throws?UnsupportedEncodingException?{????????//?生成授权码????????String?code?=?codeManager.generate(tgt,?true,?redirectUri);????????return?"redirect:"?+?authRedirectUri(redirectUri,?code);????}生成临时授权码code,并把code与codeContent内容存入map(code,content内存)
后台发起重定向请求redirect:http://client.sso.com:8082/?code=code-2f243fd967e840288ddb089611cb31c8记住这里是后端的重定向请求(前端无url变动)与response.sendRedirect(loginUrl)(前端会看到url变动)
4、跳转到OA系统进入com.yuantai.filter.LoginFilter请求拦截、进入isAccessAllowed方法的if (code != null)方法体
@Override????public?boolean?isAccessAllowed(HttpServletRequest?request,?HttpServletResponse?response)?throws?IOException?{????????SessionAccessToken?sessionAccessToken?=?SessionUtils.getAccessToken(request);????????//?本地Session中已存在,且accessToken没过期或者refreshToken成功,直接返回????????if?(sessionAccessToken?!=?null?&&?(!sessionAccessToken.isExpired()????????????????||?refreshToken(sessionAccessToken.getRefreshToken(),?request)))?{????????????return?true;????????}????????String?code?=?request.getParameter(Oauth2Constant.AUTH_CODE);????????if?(code?!=?null)?{????????????//?获取accessToken????????????getAccessToken(code,?request);????????????//?为去掉URL中授权码参数,再跳转一次当前地址????????????redirectLocalRemoveCode(request,?response);????????}????????else?{????????????redirectLogin(request,?response);????????}????????return?false;????}第一个if (sessionAccessToken != null && (!sessionAccessToken.isExpired() || refreshToken(sessionAccessToken.getRefreshToken(), request)))因为第一次登录跳转sessionAccessToken为null
5、 所以进入if (code != null)方法体
if?(code?!=?null)?{?//?获取accessToken????getAccessToken(code,?request);????//?为去掉URL中授权码参数,再跳转一次当前地址????redirectLocalRemoveCode(request,?response);}1.进去 getAccessToken(code, request);方法
private?void?getAccessToken(String?code,?HttpServletRequest?request)?{????????Result<RpcAccessToken>?result?=?Oauth2Utils.getAccessToken(getServerUrl(),?getAppId(),????????????????getAppSecret(),?code);????????if?(!result.isSuccess())?{????????????logger.error("getAccessToken?has?error,?message:{}",?result.getMessage());????????????return;????????}????????setAccessTokenInSession(result.getData(),?request);????}1.带着临时授权码code调用http://authentication.sso.com:8080/oauth2/access_token去认证中心获取用户信息、并且消费code后删除code信息(临时授权码只能生效一次) 2. 调用 setAccessTokenInSession(result.getData(), request);把登录信息存储到session中,记录session与token的映射关系
private?boolean?setAccessTokenInSession(RpcAccessToken?rpcAccessToken,?HttpServletRequest?request)?{????????if?(rpcAccessToken?==?null)?{????????????return?false;????????}????????//?记录accessToken到本地session????????SessionUtils.setAccessToken(request,?rpcAccessToken);????????//?记录本地session和accessToken映射????????recordSession(request,?rpcAccessToken.getAccessToken());????????return?true;????}6、最后调用 redirectLocalRemoveCode(request, response);去掉授权码信息带着登录成功信息再次请求http://client.sso.com:8082/
/**?????*?去除返回地址中的票据参数?????*?@param?request?????*?@return?????*?@throws?IOException?????*/????private?void?redirectLocalRemoveCode(HttpServletRequest?request,?HttpServletResponse?response)?throws?IOException?{????????String?currentUrl?=?getCurrentUrl(request);????????currentUrl?=?currentUrl.substring(0,?currentUrl.indexOf(Oauth2Constant.AUTH_CODE)?-?1);????????response.sendRedirect(currentUrl);????}总结单点登录,资源都在各个业务系统这边,不在SSO那一方。 用户在给SSO服务器提供了用户名密码后,作为业务系统并不知道这件事。 SSO随便给业务系统一个ST,那么业务系统是不能确定这个ST是用户伪造的,还是真的有效,所以要拿着这个ST去SSO服务器再问一下,这个用户给我的ST是否有效,是有效的我才能让这个用户访问 配套视频地址https://www.bilibili.com/video/BV1SR4y1P7XJ/