# 如何拦截器方式实现Oauth单点集成

  1. 定义Oauth参数

    首先在application.proterties中定义Oauth得参数 in_oauth_1 这些参数需要从第三方提供得集成文档中获取

  2. 创建Oauth构建包 in_oauth_2

  3. 创建提供接口类,监听类,拦截器实现类

  • 创建接口类,此接口提供给第三方

    @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,这个方法一般在给的集成文档的示例项目中有,如果没有可以参考当前获取方式(注意:双方人员信息须保持一致)

  1. 修改配置文件
  • 修改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>
    ...
    
  1. 修改user-config.xml,将/api/tokenLogin 设置为不拦截 in_oauth_3
上次更新: 2023/8/8下午5:44:45