# 如何拦截器方式实现Oauth单点集成
定义Oauth参数
首先在application.proterties中定义Oauth得参数 这些参数需要从第三方提供得集成文档中获取
创建Oauth构建包
创建提供接口类,监听类,拦截器实现类
创建接口类,此接口提供给第三方
@RestController public class OauthLoginTokenController { @ApiOperation("获取code") @GetMapping("/api/tokenLogin") public String tokenLogin(@ApiParam("唯一标识") @RequestParam("code") String code){ return code; } }
创建监听类,此处设置拦截器名称,位置
@RestController public class ZybxContributionListener implements IContributionListener{ @Override public void loadFinished(IContributionEvent event) { //拦截器开关,根据需求自己设置是否开启(oauth-config-control此变量需要在application.proterties中定义) String falg = EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-control"); if("true".endsWith(falg)) { // 增加一个eos资源拦截器,用于权限码判断 String id = "ZybxUserLoginInterceptor"; //拦截器类名称 WebInterceptorConfig config = new WebInterceptorConfig(); config.setFilterId(id); config.setClassName(ZybxUserLoginInterceptor.class.getName()); //拦截器类 config.setPattern("/*"); config.setSortIdx(200); WebInterceptorManager.INSTANCE.removeInterceptorConfig(id);//防止冲突 WebInterceptorManager.INSTANCE.addInterceptorConfig(config); System.out.println("执行拦截器-----1"); } } }
创建拦截器实现类
public class ZybxUserLoginInterceptor implements IWebInterceptor { /** * .getLogger(ZybxUserLoginInterceptor.class);pms_all */ private static final Logger log = LoggerFactory.getLogger(ZybxUserLoginInterceptor.class); public static final String AUTHORIZATION = "Authorization"; private static ILoginService loginService; private final static List<String> ADMIN_ROLES = Arrays.asList("1","2","3","4","5","6","7","8","9"); static { BeanFactory beanFactory = BeanFactory.newInstance(); loginService = beanFactory.getBean(ILoginService.class); } @Override public void doIntercept(HttpServletRequest request, HttpServletResponse response, IWebInterceptorChain chain) throws IOException, ServletException { DataContextManager.current().setMapContextFactory(new HttpMapContextFactory(request, response)); String servletPath = HttpUrlHelper.getRequestUrl(request, response); //加个请求头过滤,管理员不能登录 String header = request.getHeader("x-pms-origin"); log.info("header:{}",header); if (!StrUtil.isBlank(header)){ //这里要判断当前登录人的角色,如果是管理员角色,就不允许访问 IUserObject userObject = DataContextService.current().getUserObject(); if (ObjectUtil.isNull(userObject)){ log.error("当前登录信息为空,未登录"); throw new RuntimeException("当前登录信息为空,未登录"); } String userRoles = (String) userObject.getAttributes().get("userRoles"); log.info("userRoles:{}",userRoles); if (!StrUtil.isBlank(userRoles)){ JSONArray jsonArray = JSONUtil.parseArray(userRoles); jsonArray.retainAll(ADMIN_ROLES); if (jsonArray.size()>0){ log.error("当前登录人是从外网登录,禁止访问"); throw new RuntimeException("当前登录人" +userObject.getUserName()+ "是从外网登录,禁止访问"); } } } log.info("--------------doIntercept-------------{}",servletPath); String property = EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-control"); if ("true".equals(property)&&HttpUrlHelper.matchFiltersURL("/api/tokenLogin",servletPath)){ log.info("----------进入oauth拦截器-------------{}",servletPath); String code = request.getHeader("code"); if (StringUtils.isBlank(code)) { // 请求头为空,则从请求参数中获取 code = request.getParameter("code"); } log.info("code,{}",code); log.info("------------未登录----------"); //如果code为空,需要重定向至获取code地址 if(!StringUtils.isNotBlank(code)) { String url = EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-get-code-url") + "?response_type=code&client_id=" + EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-id") + "&redirect_uri=" + EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-redirect-url"); log.info("code为空,重定向到统一认证:{}",url); response.sendRedirect(url); return; }else { String accessToken = getAccessToken(code); log.info("accessToken:{}",accessToken); String userid = getUsernameByToken(accessToken); log.info("userid:{}",userid); UserObject user = loginService.createUserObjectWithUserId(userid); OnlineUserManager.login(user); String urlParam = AUTHORIZATION+"="+user.getUniqueId(); response.sendRedirect(EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-afc-index-url")+"?"+urlParam); return; } } chain.doIntercept(request,response); } private final OkHttpClient okHttpClient = new OkHttpClient.Builder().build(); private String getAccessToken(String code) throws IOException { Map<String,Object> paramMap = new HashMap<>(5); paramMap.put("grant_type","authorization_code"); paramMap.put("client_id",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-id")); paramMap.put("client_secret",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-secret")); paramMap.put("code",code); paramMap.put("redirect_uri",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-redirect-url")); String urlParam = HttpUtil.toParams(paramMap); Request request = new Request.Builder() .url(EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-code-url")+"?"+urlParam) .get() .build(); try (Response response = okHttpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } ResponseBody body = response.body(); if (!ObjectUtil.isNotNull(body)) { throw new IOException("返回报文为空"); } String bodyStr = body.string(); JSONObject jsonObject = JSONUtil.parseObj(bodyStr); return (String) jsonObject.get("access_token"); } } private String getUsernameByToken(String accessToken) throws IOException { String url = EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-url")+"?access_token=" + accessToken; Request request = new Request.Builder() .url(url) .get() .build(); try (Response response = okHttpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } ResponseBody body = response.body(); if (!ObjectUtil.isNotNull(body)) { throw new IOException("返回报文为空"); } String bodyStr = body.string(); JSONObject jsonObject = JSONUtil.parseObj(bodyStr); return (String) jsonObject.get("id"); } } protected void initContext(HttpServletRequest request, IUserObject userObject, HttpSession session) { session.setAttribute(IUserObject.KEY_IN_CONTEXT, userObject); //根据session创建MUO,并初始化上下文 IMUODataContext muo = MUODataContextHelper.create(session); DataContextManager.current().setMUODataContext(muo); } }
此处实现思路第三方验证系统调用/api/tokenLogin接口,拦截器拦截当前接口,获取code,通过code获取accessToken,拿到accessToken后,根据accessToken获取用户信息(对方传过来的userid),用userid调用afc登录接口,,进行免登录操作,最后重定向到afc首页
获取code
... String code = request.getHeader("code"); if (StringUtils.isBlank(code)) { // 请求头为空,则从请求参数中获取 code = request.getParameter("code"); } log.info("code,{}",code); log.info("------------未登录----------"); //如果code为空,需要重定向至获取code地址 if(!StringUtils.isNotBlank(code)) { String url = EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-get-code-url") + "?response_type=code&client_id=" + EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-id") + "&redirect_uri=" + EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-redirect-url"); log.info("code为空,重定向到统一认证:{}",url); response.sendRedirect(url); return; } ...
code可以从请求头中获取,也可以从请求参数中获取,如果这两个里面都没有获取到,需要从给的文档中获取code的url中获取code
获取accessToken
... private String getAccessToken(String code) throws IOException { Map<String,Object> paramMap = new HashMap<>(5); paramMap.put("grant_type","authorization_code"); paramMap.put("client_id",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-id")); paramMap.put("client_secret",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-secret")); paramMap.put("code",code); paramMap.put("redirect_uri",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-redirect-url")); String urlParam = HttpUtil.toParams(paramMap); Request request = new Request.Builder() .url(EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-code-url")+"?"+urlParam) .get() .build(); try (Response response = okHttpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } ResponseBody body = response.body(); if (!ObjectUtil.isNotNull(body)) { throw new IOException("返回报文为空"); } String bodyStr = body.string(); JSONObject jsonObject = JSONUtil.parseObj(bodyStr); return (String) jsonObject.get("access_token"); } } ...
获取accessToken,这个方法一般在给的集成文档的示例项目中有,如果没有可以参考当前获取方式
获取userInfo,用户信息
... private String getAccessToken(String code) throws IOException { Map<String,Object> paramMap = new HashMap<>(5); paramMap.put("grant_type","authorization_code"); paramMap.put("client_id",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-id")); paramMap.put("client_secret",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-client-secret")); paramMap.put("code",code); paramMap.put("redirect_uri",EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-redirect-url")); String urlParam = HttpUtil.toParams(paramMap); Request request = new Request.Builder() .url(EOS8ApplicationContext.INSTANCE.getProperty("oauth-config-code-url")+"?"+urlParam) .get() .build(); try (Response response = okHttpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } ResponseBody body = response.body(); if (!ObjectUtil.isNotNull(body)) { throw new IOException("返回报文为空"); } String bodyStr = body.string(); JSONObject jsonObject = JSONUtil.parseObj(bodyStr); return (String) jsonObject.get("access_token"); } } ...
获取获取userInfo,这个方法一般在给的集成文档的示例项目中有,如果没有可以参考当前获取方式(注意:双方人员信息须保持一致)
- 修改配置文件
- 修改Oauth包下的contribution.eosinf,定义拦截器所在位置
... <module name="WebInterceptor"> <group name="ZybxUserLoginInterceptor"> <configValue key="id">ZybxUserLoginInterceptor</configValue> <configValue key="sortIdx">8</configValue> <configValue key="pattern">/*</configValue> <configValue key="class">com.chinapost.life.pms.oauth.ZybxUserLoginInterceptor</configValue> </group> </module> ...
- 修改Oauth包下的handler-contribution.xml,定义监听器所在位置
... <handlers> <handler handle-class="com.chinapost.life.pms.oauth.ZybxContributionListener"/> </handlers> ...
- 修改user-config.xml,将/api/tokenLogin 设置为不拦截