精华内容
下载资源
问答
  • Springboot使用拦截器进行token的登录验证
    千次阅读
    2021-03-18 00:24:44

    import java.io.IOException;

    import javax.servlet.Filter;

    import javax.servlet.FilterChain;

    import javax.servlet.FilterConfig;

    import javax.servlet.ServletException;

    import javax.servlet.ServletRequest;

    import javax.servlet.ServletResponse;

    import javax.servlet.annotation.WebFilter;

    import javax.servlet.http.HttpServletRequest;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    import org.springframework.stereotype.Component;

    @Component

    @WebFilter(urlPatterns = { "/*" }, filterName = "tokenAuthorFilter")

    public class TokenAuthorFilter implements Filter {

    private static Logger logger = LoggerFactory.getLogger(TokenAuthorFilter.class);

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

    throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest) request;

    String token = req.getHeader("token");

    logger.info("token:{}", token);

    if (null == token || token.isEmpty()) {

    throw new ServletException("用户授权认证没有通过!客户端请求参数中无token信息");

    } else {

    if (this.volidateToken(token)) {

    //通过校验

    logger.info("token过滤ok!");

    chain.doFilter(request, response);

    } else {

    throw new ServletException("用户授权认证没有通过!客户端请求参数token信息无效");

    }

    }

    }

    public boolean volidateToken(String token) {

    //对token内包含的用户名以及密码进行校验

    if(true) {

    return true;

    }

    return false;

    }

    @Override

    public void destroy() {}

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {}

    }

    更多相关内容
  • 使用拦截器验证token是否有效

    千次阅读 2020-03-30 17:14:00
    2、符合路径并且添加了注解的接口发送请求时会进入拦截器拦截器负责比对传入的token是否正确(暂未加密处理); 3、正确则继续,否则直接返回JSON。 1.Configuration import cn.ac.bcc.ebap...

    最近项目中需要做每一个接口均加token参数,web端进行验证。

    我实用的是拦截器。

    1、整体思路是定义好需要拦截的路径,并将使用的接口添加@ApiToken

    2、符合路径并且添加了注解的接口发送请求时会进入拦截器,拦截器负责比对传入的token是否正确(暂未加密处理);

    3、正确则继续,否则直接返回JSON。

    1.Configuration

    import cn.ac.bcc.ebap.common.interceptor.WebApiInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

    @Configuration
    public class AppTockenConfiguration  extends WebMvcConfigurerAdapter {

        @Bean
        public WebApiInterceptor webApiInterceptor(){
            return  new WebApiInterceptor ();
        }

        @Override
        public  void addInterceptors(InterceptorRegistry registry){
            //多个拦截器组成一个拦截器链
            //addPathPattern 用于添加拦截规则 路径,是带api接口的
            //用于定义、排除用户的拦截
            registry.addInterceptor(webApiInterceptor())
                    .addPathPatterns("/a/depart/**");
    //                .excludePathPatterns("/a/login");
            super.addInterceptors(registry);
        }


    }

     

    2.WebApiInterceptor

    这边返回值直接返回JSON。

    import cn.ac.bcc.ebap.common.annotation.ApiToken;
    import com.alibaba.fastjson.JSONObject;
    import org.apache.log4j.Logger;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.PrintWriter;
    import java.lang.reflect.Method;

    public class WebApiInterceptor extends HandlerInterceptorAdapter {
        private static final Logger log = Logger.getLogger(WebApiInterceptor.class);

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            String sessionId = request.getSession ().getId ();
            if(sessionId == null){
                log.info("sessionId 已失效");
    //                    throw new RuntimeException ();
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=utf-8");
                PrintWriter out = null ;
                JSONObject res = new JSONObject ();
                res.put("resultCode",302);
                res.put("message","sessionId 已失效");
                out = response.getWriter();
                out.append(res.toString());
                return false;
            }

            String token = request.getParameter ("access_token");
            log.info("Token:{" + token + "}; 请求路径:{" + request.getRequestURI() + "}");
            if (method.isAnnotationPresent(ApiToken.class)) {
                if (token != null) {
                    if(token.equals (sessionId)){
                        return true;
                    }else{
                        log.info("token不可用");
    //                    throw new RuntimeException ();
                        response.setCharacterEncoding("UTF-8");
                        response.setContentType("application/json; charset=utf-8");
                        PrintWriter out = null ;
                        JSONObject res = new JSONObject ();
                        res.put("code",300);
                        res.put("message","token不可用");
                        out = response.getWriter();
                        out.append(res.toString());
                        return false;
                    }
                }else{
                    log.info("token不可为空");
    //                    throw new RuntimeException ();
                    response.setCharacterEncoding("UTF-8");
                    response.setContentType("application/json; charset=utf-8");
                    PrintWriter out = null ;
                    JSONObject res = new JSONObject ();
                    res.put("code",301);
                    res.put("message","token不可为空");
                    out = response.getWriter();
                    out.append(res.toString());
                    return false;
                }
            }
            return true;
        }

        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

        }

        //方法执行之后拦截
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            System.out.println("========方法执行之后 开始调用===============");
        }

    }
     

    3. 注解

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    /**
     * Created by zhanghaipeng on 2020/3/30.
     */
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiToken {
    }

     

    ps:可以模拟JWT进一步优化,如加入过期时间参数等。

    展开全文
  • java的jwt+拦截器校验token获取用户信息 最近换工作,发现自己只会crud准备加强一下。结合网上资源写了一个用jwt生成并且校验token、校验token是在拦截器里面的,还有对异常的封装,响应体返回的封装,先写代码再...

    java的jwt+拦截器校验token获取用户信息

    最近换工作,发现自己只会crud准备加强一下。结合网上资源写了一个用jwt生成并且校验token、校验token是在拦截器里面的,还有对异常的封装,响应体返回的封装,先写代码再总结一下项目中实际可以优化的地方。
    异常封装和响应体封装都在上一节内容,地址:https://blog.csdn.net/yechuanjiang/article/details/120413850?spm=1001.2014.3001.5501
    这里写的是生成token和用拦截器校验token,和用token获取登陆用户,希望到这里的兄弟都有一个思想,代码实现多种做样,实现目标的方法多种多样

    第一个类试是jwt的工具类,用于生成token、校验token的正确性和有效期、用token获取当前登陆用户id

    package com.example.jwt2021921.utils;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.exceptions.JWTVerificationException;
    import com.auth0.jwt.exceptions.TokenExpiredException;
    import com.example.jwt2021921.entity.User;
    import lombok.extern.log4j.Log4j2;
    
    import java.util.Date;
    
    /**
     * @Auther: 叶川江
     * @Date: 2021/9/18 12:25
     * @Description: 个人v: ycj940729
     */
    @Log4j2
    public class GetToken {
    //加密密钥
        private static final String SECRET = "Axmk89Li3Aji9M";
        //创建token
        public static String createToken(User user) {
            
            String token="";
            Date date = new Date(System.currentTimeMillis() + 20*1000);
            token= JWT.create()
                    .withExpiresAt(date)//token有效期
                    .withSubject(user.getId().toString())//保存用户id,user类自己i而一个
                    .sign(Algorithm.HMAC256(SECRET));//加密密钥
            return token;//返回token
        }
        //这个用于在拦截器的时候校验token的有效期和正确性
        public static boolean verifyToken(String token){
            try{
                JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
                verifier.verify(token);
                return true;
            }catch(Exception e){
                throw new TokenExpiredException("token失效");
            }
        }
        /**
         * 验证token.并且登录用户id
         */
        public static String getUserByToken(String token) throws Exception {
            try {
                String subject = JWT.require(Algorithm.HMAC256(SECRET))
                        .build()
                        .verify(token)
                        .getSubject();
                return subject;
            } catch (TokenExpiredException e){
                throw new Exception("token已失效,请重新登录",e);
            } catch (JWTVerificationException e) {
                throw new Exception("token验证失败!",e);
            }
        }
    }
    
    

    第二个就是拦截器,有两个类

    package com.example.jwt2021921.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * @Auther: 叶川江
     * @Date: 2021/9/18 12:44
     * @Description: 拦截器,拦截所有资源
     */
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/api/get")//拦截路径
            .excludePathPatterns("/api/login");//放行路径
        }
        // 由于拦截器执行顺序的问题,必要要把第二个类加入容器,不然会报错,具体的解释我忘记了,旨在网上看到郭义熙,可以自行查阅
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    }
    
    
    package com.example.jwt2021921.config;
    
    import com.example.jwt2021921.utils.GetToken;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Method;
    
    /**
     * @Auther: 叶川江
     * @Date: 2021/9/18 12:26
     * @Description:  用拦截器校验token
     */
    public class AuthenticationInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
            String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
            // 如果不是映射到方法直接通过
    //        if (!(object instanceof HandlerMethod)) {
    //            return true;
    //        }
    //        HandlerMethod handlerMethod = (HandlerMethod) object;
    //        Method method = handlerMethod.getMethod();
    
            boolean b = GetToken.verifyToken(token);//这里就是在拦截器校验token了
    
    
            return b;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse,
                               Object o, ModelAndView modelAndView) throws Exception {
    
        }
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    Object o, Exception e) throws Exception {
        }
    }
    
    

    下面这个自己写一个controller按照你拦截路径的规则定义路径就可以了

    package com.example.jwt2021921.controller;
    
    import com.baomidou.mybatisplus.core.toolkit.StringUtils;
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
    import com.example.jwt2021921.core.RestResponse;
    import com.example.jwt2021921.entity.User;
    import com.example.jwt2021921.service.UserService;
    import com.example.jwt2021921.utils.GetToken;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    
    /**
     * @Auther: 叶川江
     * @Date: 2021/9/21 01:22
     * @Description:
     */
    @RestController
    @RequestMapping("/api")
    public class UserLogin {
    
        @Resource
        private UserService userService;//这个service可以不用管,自己写死一个实体也可以测试
        //第一个方法就是登录路径,这个在我拦截器的时候是没有进行拦截的,一般登录也不需要拦截,这个类就是生成token返回给前端,以后前端每次调用接口都要把token给我
        @GetMapping("/login")
        public RestResponse login(@RequestBody User user) throws Exception {
            User one = userService.getOne(Wrappers.lambdaQuery(User.class)
                    .eq(User::getUsername, user.getUsername()));
            if (null ==one){
                return RestResponse.failed("账户错误");
            }
            String token = GetToken.createToken(one);
            System.out.println(token);
            String userId = GetToken.getUserByToken(token);
            return RestResponse.ok(token);
        }
    
    //这个类就是拦截器被拦截的路径,如果token不对,或者过起就会显示token失效或者错误
        @GetMapping("/get")
        public RestResponse get(){
            return RestResponse.ok("yechuanjiang");
        }
    }
    
    

    最后总结个人意见把,这里只是个人简单的demo,
    不知道实际项目中会不会有些这么些哈,并发如果多的话,拦截器会不会有问题,还有就是token我这里只是存了用户id,其他信息需要在调一次sql查询,还有就是权限的问题,这里可以用redis,token做k,用户信息做v

    展开全文
  • springboot 拦截器验证token

    千次阅读 2019-11-08 17:17:11
    springboot 配置拦截器验证token并返回json给前端。代码如下: /** * 验证token,是否登录 */ @Component public class TokenInterceptor implements HandlerInterceptor { private Logger logger = ...

    springboot 配置拦截器,验证token并返回json给前端。代码如下:

    /**
     * 验证token,是否登录
     */
    @Component
    public class TokenInterceptor implements HandlerInterceptor {
    
        private Logger logger = LoggerFactory.getLogger(TokenInterceptor.class);
        /**
         * 忽略拦截的url
         */
        private String urls[] = {
                "/login",
                "/register"
        };
        @Autowired
        private UserService userService;
        /**
         * 进入controller层之前拦截请求
         * @param httpServletRequest
         * @param httpServletResponse
         * @param o
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
            String url = httpServletRequest.getRequestURI();
            String token = httpServletRequest.getHeader("token");
            String method = httpServletRequest.getMethod();
            if (!method.equals("OPTIONS")){
                logger.info(token);
                logger.info(url);
                logger.info(method);
                // 遍历需要忽略拦截的路径
                for (String item : this.urls){
                    if (item.equals(url)){
                        return true;
                    }
                }
                // 查询验证token
                User userByToken = userService.getUserByToken(token);
                if (userByToken == null){
                    httpServletResponse.setCharacterEncoding("UTF-8");
                    httpServletResponse.setContentType("application/json; charset=utf-8");
                    PrintWriter out = null ;
                    try{
                        Result res = new Result(10001,"登录失效重新登录");
                        String json = JSON.toJSONString(res);
                        httpServletResponse.setContentType("application/json");
                        out = httpServletResponse.getWriter();
                        // 返回json信息给前端
                        out.append(json);
                        out.flush();
                        return false;
                    } catch (Exception e){
                        e.printStackTrace();
                        httpServletResponse.sendError(500);
                        return false;
                    }
                }
                return true;
            }
            return false;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
            // System.out.println("处理请求完成后视图渲染之前的处理操作");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
            // System.out.println("视图渲染之后的操作");
        }
    }
    
    

    注册拦截器

    /**
     * 注册拦截器
     */
    @Configuration
    public class WebAppConfig implements WebMvcConfigurer {
        @Autowired
        private CROSInterceptor crosInterceptor;
    
        @Autowired
        private TokenInterceptor tokenInterceptor;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(crosInterceptor).addPathPatterns("/**");   // 跨域拦截器
            registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");  // token 验证拦截器
        }
    }
    

    用到的包

    <!-- json 转换工具 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.29</version>
    </dependency>
    
    展开全文
  • // 添加请求拦截器 axios.interceptors.request.use(function (config) { // 在发送请求之前添加请求头 config.headers.Authorization = window.sessionStorage.getItem('token'); return config }, function ...
  • 拦截器实现token校验

    2021-06-21 11:18:06
    * token校验拦截器 * * @author LIUQI */ @Component public class RepeatSubmitInterceptor extends HandlerInterceptorAdapter { @Resource RedisUtil redisUtil; @Override public boolean preHandle...
  • 拦截器token验证+权限

    千次阅读 2021-06-11 14:25:14
    //静态资源 //注入拦截器 registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录 // .excludePathPatterns("/user")//不拦截...
  • 添加拦截器 1.创建一个interceptor包,创建LoginInterceptor.java和WebConfig.java WebConfig.java: package com.hospital.total_managed.interceptor; import org.springframework.context.annotation....
  • SpringBoot2中添加拦截器验证token

    千次阅读 2020-05-08 14:13:44
    前言 基础知识说明 token是无状态的,帮助你验证用户是否具有查询api的权限,一般登录后即可生成。 拦截器下,解决swagger...IAuthService 是我验证token的服务类,可自行编写 R.failed() 是mybatis-plus包中 filte...
  • token登录验证拦截器实现登陆

    千次阅读 2021-03-11 11:45:17
    Token认证登录(原创有效) ...5.服务端验证token,校验成功则返回请求数据,校验失败则返回错误码。 上代码… token工具类 package com.jk.dup.util; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier;
  • SpringBoot 自定义拦截器 检验token并返回前端请求参数 String json = new ObjectMapper().writeValueAsString("map"); response.setContentType("application/json;charset=UTF-8"); response.getWriter().println...
  • OKhttp 拦截器Intercept token失效验证

    千次阅读 2017-04-22 15:00:39
    OKhttp 拦截器Intercept token失效验证,我相信现在很多人都在用OKhttp作为网络请求库,为什么OKhttp会受到这么多人的青睐呢,谷歌也不例外(据我了解到它是唯一一个被谷歌认可的第三方网络请求库), 原因有很多,...
  • Java基于JWT的token认证

    2021-03-18 00:24:42
    一、背景引入由于Http协议本身是无状态的,那么服务器是怎么识别两次请求是不是来自同一个客户端呢,传统用户识别是基于seesion和cookie实现的。大致流程如下:用户向服务器发送用户名和密码请求用户进行校验,校验...
  • springboot + 拦截器 验证token 通常做项目的登陆接口时,会生成唯一token值供后期的请求验证。 目前做法是: 1、用户登陆时,将用户信息以及token值保存在redis 2、项目中添加拦截器,获取请求路径以及相关请求参数...
  • 问题描述对于Spring mvc的拦截器我觉的应该是请求什么就该拦截什么,没有请求的就不该被拦截,但是不知道为什么,在如下的代码中,当我请求/路径的时候,我并没有请求index.html呀,为什么执行完控制器的代码和拦截...
  • 这个我遇到的情况有所不同,我的情况是进入拦截器后,然后进入请求请求中返回的内容没有做@ResponseBody标识, 就会再一次进入到拦截器中去。 细节决定成败,问题不大,找了两三个小时,在这里记录一下 ...
  • /*************** * token验证拦截 *@authorbamboo zjcjava@163.com * @time 2017-08-01*/@Component//@WebFilter(urlPatterns = { "/api/v/*" }, filterName = "tokenAuthorFilter") public class ...
  • java过滤全局解析token使用过滤定义一个全局的token解析在进行后端接口的开发过程中,一般涉及到人员用户,权限或者安全方面的考虑接口都会使用token来传递用户或者一些安全系数高的鉴权参数等。一般接口定义...
  • springboot自定义拦截器,校验token

    千次阅读 2022-01-29 17:49:14
    一、自定义拦截器 package com.example.demo.test; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import org.springframework.beans.factory.annotation.Autowired; import org....
  • 最近项目中需要对一些特定的请求进行验证,所以自己写了拦截器用于对token进行特定的验证处理。 token放在请求头中 客户端发送请求 使用拦截器进行拦截 处理token 通过实现HandlerInterceptor生成一个拦截器 @...
  • 1.springboot 拦截器处理过滤token,并且返回结果import org.apache.commons.lang3.StringUtils;import org.apache.shiro.subject.Subject;import org.springframework.lang.Nullable;import org.springframework....
  • 第一步:自定义注解 @Retention(RetentionPolicy.RUNTIME) ...第二步:自定义拦截器,实现 HandlerInterceptorAdapter @Component @Slf4j public class AuthorizationInterceptor extends Hand
  • 第一步:创建一个实现 HandlerInterceptor 的类 import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView;...到此一个简易的拦截器就实现了
  • 自定义拦截器实现对项目所有请求url的拦截与过滤,结合自定义token机制,用redis控制token的失效时间。同时配置文件里面增加token启停的开关,可以用来控制是否启停token的功能。 token的传输规则为:前端请求头中...
  • 配置拦截器校验token

    2020-09-30 10:55:58
    1.拦截器配置 代码如下(示例): /** * mvc拦截器 */ @Configuration public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry...
  • * @Description:检查是否忽略token * @Author: zzl * @Date: 2021/6/4 11:13 * @Param: [request] * @Return: boolean **/ private Request checkIgnoreToken(Request request) { try { boolean ignoreToken...
  • 一、为什么需要拦截器? 在前后端分离的现在,项目中的所有的前端的页面都需要通过调用后台的Api进行获取数据 接口的功能点不同,就会有很多种情况,比如...所以说需要一个拦截器去区分哪些路径下需要token校验,那...
  • https://blog.csdn.net/qq_30385099/article/details/105252609 跨域请求,找不到token拦截器一个url拦截两次问题
  • Java拦截器验证失败时返回Json格式数据 1、添加FastJson依赖 在pom.xml中添加如下依赖 <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson<...
  • 在之前的一个项目中我用springsecurity框架写过类似的功能,但是直接copy进来这样太重了,而且代码太繁琐,所以我就自己写了个自定义的拦截器。 2.自定义拦截器的配置 ​ 1.实现HandlerInterceptor接口,重写...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 16,817
精华内容 6,726
关键字:

java请求拦截器验证token

java 订阅