精华内容
下载资源
问答
  • 正版spring security...OAuth 2.0 如何获取令牌以密码模式为例,获取 Tokencurl --location --request POST 'http://oauth-server/oauth/token' \--header 'Authorization: Basic dGVzdDp0ZXN0' \--data-urlencode...

    正版spring security实战编程与

    54.9元

    (需用券)

    去购买 >

    a558c348652cbcf13105633fc3598b19.png

    OAuth 2.0 如何获取令牌

    以密码模式为例,获取 Token

    curl --location --request POST 'http://oauth-server/oauth/token' \

    --header 'Authorization: Basic dGVzdDp0ZXN0' \

    --data-urlencode 'username=admin' \

    --data-urlencode 'password=123456' \

    --data-urlencode 'scope=server' \

    --data-urlencode 'grant_type=password'

    {

    "access_token":"2YotnFZFEjr1zCsicMWpAA",

    "token_type":"example",

    "expires_in":3600,

    "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",

    "example_parameter":"example_value"

    }

    原流程其实去访问 OAuth 2.0 提供的 /oauth/token 源码如下

    TokenEndpoint.postAccessToken

    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)

    public ResponseEntity postAccessToken(Principal principal, @RequestParam

    Map parameters){

    ...

    return getResponse(token);

    }

    自定义默认获取令牌地址

    如上文,默认情况下我们需要访问 /oauth/token 获取,也就是所有业务系统的 “登录”接口 都变成这个地址,如何在不重写此接口的情况下,自定义路径地址。

    Spring Security OAuth2 为我们提供了丰富的 配置,我们可以在 AuthorizationServerConfigurerAdapter 设置所有内置端点 (Endpoint)路径的自定义 pathMapping

    如下 使用 /pig4cloud/login 覆盖 原有的/oauth/token,注意这里是覆盖一旦配置 原有路径将失效

    @EnableAuthorizationServer

    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override

    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

    endpoints

    .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)

    .pathMapping("/oauth/token","/pig4cloud/login"); }

    }

    获取令牌地址就变成如下

    curl --location --request POST 'http://oauth-server/pig4cloud/login' \

    --header 'Authorization: Basic dGVzdDp0ZXN0' \

    --data-urlencode 'username=admin' \

    --data-urlencode 'password=123456' \

    --data-urlencode 'scope=server' \

    --data-urlencode 'grant_type=password'

    源码剖析

    spring security oauth2 是如何实现 这种端点自定义配置的呢?

    AuthorizationServerEndpointsConfigurer 被写入到自定义 HandlerMapping

    private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping() {

    if (frameworkEndpointHandlerMapping == null) {

    frameworkEndpointHandlerMapping = new FrameworkEndpointHandlerMapping();

    frameworkEndpointHandlerMapping.setMappings(patternMap);

    frameworkEndpointHandlerMapping.setPrefix(prefix);

    frameworkEndpointHandlerMapping.setInterceptors(interceptors.toArray());

    }

    return frameworkEndpointHandlerMapping;

    }

    SpringMVC DispatcherServlet 会根据新的规则 进行路由

    java 11官方入门(第8版)教材

    79.84元

    包邮

    (需用券)

    去购买 >

    f0f3f55624fb396b1764d42d6df88864.png

    展开全文
  • 在高并发系统中,存在着巨大的挑战,大流量高并发的访问...对服务器端访问频率较多的查询接口做redis缓存,减小数据库的压力;限流今天我自己就来学习一下关于如何做到限流。起初我想的是对请求接口做切面处理,获取...

    在高并发系统中,存在着巨大的挑战,大流量高并发的访问。一些常见的有天猫的双十一、京东618、秒杀以及延时促销等。短时间内的如此巨大的访问流量往往会给数据库造成巨大的压力,进而影响服务器端的稳定性,那么我们的解决方案包括有:

    前端用nginx做负载均衡;

    对服务器端访问频率较多的查询接口做redis缓存,减小数据库的压力;

    限流

    今天我自己就来学习一下关于如何做到限流。起初我想的是对请求接口做切面处理,获取发送请求端的IP,然后将IP、请求的接口路径、时间等信息存入redis,然后对每一次的请求和redis中存入的信息进行比对,如果IP相同且两次间隔时间很小,那么我们就可以直接返回结果而不进入到controller层,当然也可以存入mysql数据库中,两者区别是,mysql中是持久化的,而redis中会因为服务器挂掉等原因造成数据丢失,当然其实影响也不大,因为限流主要就要保证某一时间点内对访问请求进行限制,所以相关信息存入redis的保存时间不用过长,这些都可以根据实际情况来予以处理。

    然而查阅相关资料,了解到令牌桶,所以就自己实现一下用令牌桶来限流。关于令牌桶的一些知识,有兴趣的可以去了解一下。

    首先,构建springboot项目,我使用的idea2018 +jdk1.8。项目文件目录如图所示:

    876dfd37d1ca06d8ac67ab8118d54b59.png

    接着就是controller层、service层、dao层的代码编写,

    importcom.white.ratelimiter.service.MassageService;importcom.white.ratelimiter.service.PayService;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.math.BigDecimal;importjava.util.concurrent.TimeUnit;/*** 功能模块()

    *

    * @Author white Liu

    * @Date 2020/7/31 10:10

    * @Version 1.0*/@RestController

    @RequestMapping("/api")

    @Slf4jpublic classPayController {

    @Autowired

    PayService payService;

    @Autowired

    MassageService massageService;/*** RateLimiter.create(2)表示以固定速率2r/s,即以每秒2个令牌的速率放至到令牌桶中*/

    private RateLimiter rateLimiter = RateLimiter.create(1);

    @GetMapping("/pay/get")publicBaseResult pay(){//限流处理,客户端请求从桶中获取令牌,如果在500毫秒没有获取到令牌,则直接走服务降级处理

    boolean tryAcquire = rateLimiter.tryAcquire(500, TimeUnit.MILLISECONDS);if(!tryAcquire){

    log.info("请求过多,请稍后");return BaseResult.error(500,"当前请求过多!");

    }int ref = payService.doPay(BigDecimal.valueOf(99.8));if(ref>0){

    log.info("支付成功");return BaseResult.ok().put("msg","支付成功");

    }return BaseResult.error(400,"支付失败,请重新尝试");

    }

    }

    启动程序后,用浏览器请求接口,然后再不断快速刷新,页面会显示如下图所示,可见令牌桶限流能够生效!

    125899564da6e521b06b82ad63700d3c.png

    下面第二个接口尝试优化代码量,使用自定义注解以及切面类来实现令牌桶限流。

    首先自定义注解:

    packagecom.white.ratelimiter.annotation;/*** 功能模块(令牌桶限流注解)

    *

    * @Author white Liu

    * @Date 2020/8/1 11:55

    * @Version 1.0*/

    import java.lang.annotation.*;

    @Documented

    @Target(value=ElementType.METHOD)

    @Retention(RetentionPolicy.RUNTIME)public @interfaceMyLimiter {//向令牌桶放入令牌的速率

    doublerate();//从令牌桶获取令牌的超时时间

    long timeout() default 0;

    }

    接着编写切面类,获取使用该自定义注解的接口,然后使用令牌桶来对该接口进行限流

    packagecom.white.ratelimiter.aspect;importcom.google.common.util.concurrent.RateLimiter;importcom.white.ratelimiter.annotation.MyLimiter;importlombok.extern.slf4j.Slf4j;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.aspectj.lang.annotation.Pointcut;importorg.aspectj.lang.reflect.MethodSignature;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Scope;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;importjava.io.PrintWriter;importjava.util.concurrent.TimeUnit;/*** 功能模块()

    *

    * @Author white Liu

    * @Date 2020/8/1 11:59

    * @Version 1.0*/@Component

    @Aspect

    @Slf4j

    @Scopepublic classMyRateLimiterAspect {

    @Autowiredprivate HttpServletResponse response;//注入HttpServletResponse对象,进行降级处理,返回必要信息提示用户

    private RateLimiter rateLimiter = RateLimiter.create(1);

    @Pointcut("execution(public * com.white.ratelimiter.controller.*.*(..))")public voidcutPoint(){}

    @Around("cutPoint()")public Object process(ProceedingJoinPoint proceedingJoinPoint) throwsThrowable {

    MethodSignature signature=(MethodSignature) proceedingJoinPoint.getSignature();//使用反射获取方法上是否存在@MyRateLimiter注解

    MyLimiter myRateLimiter = signature.getMethod().getDeclaredAnnotation(MyLimiter.class);if(myRateLimiter == null){//程序正常执行,执行目标方法

    returnproceedingJoinPoint.proceed();

    }//获取注解上的参数//获取配置的速率

    double rate =myRateLimiter.rate();//获取客户端等待令牌的时间

    long timeout =myRateLimiter.timeout();//设置限流速率

    rateLimiter.setRate(rate);//判断客户端获取令牌是否超时

    boolean tryAcquire =rateLimiter.tryAcquire(timeout, TimeUnit.MILLISECONDS);if(!tryAcquire){

    log.info("当前访问太多,请稍后尝试");//服务降级

    callback();return null;

    }//获取到令牌,直接执行

    returnproceedingJoinPoint.proceed();

    }/*** 降级处理*/

    private voidcallback() {

    response.setHeader("Content-type", "text/html;charset=UTF-8");

    PrintWriter writer= null;try{

    writer=response.getWriter();

    writer.println("出错了,请重新尝试");

    writer.flush();;

    }catch(IOException e) {

    e.printStackTrace();

    }finally{if(writer != null){

    writer.close();

    }

    }

    }

    }

    然后在接口处添加自定义注解

    @GetMapping("/msg/get")

    @MyLimiter(rate= 1.0,timeout = 500)publicBaseResult sendMsg(){boolean flag = massageService.sendMsg("恭喜您成长值+1");if(flag){

    log.info("消息发送成功");return BaseResult.ok("消息发送成功");

    }return BaseResult.error(400,"消息发送失败,请重新尝试一次吧...");

    }

    至此,自定义注解限流完成,启动程序,打开浏览器,输入rul,得到下图所示

    dd7752c360ab1925773e5db6422c8404.png

    尝试不断快速刷新页面,自定义注解会生效,页面会显示

    8358434ab7e80b642ece6cff7e788a88.png

    github代码下载地址:点击这里

    展开全文
  • 我已经安装了Keycloak服务器,并且可以使用以下命令访问Web UI:http:// localhost:8008 / auth我的要求是通过将其传递给k来验证领域用户Keycloak API并从那里获取令牌作为响应,然后将此令牌传递给我的其他Web ...

    首先,我对Keycloak非常陌生,请问如果我要问的问题可能是错的。

    我已经安装了Keycloak服务器,并且可以使用以下命令访问Web UI:

    http:// localhost:8008 / auth

    我的要求是通过将其传递给k来验证领域用户

    Keycloak API并从那里获取令牌作为响应,然后将此令牌传递给我的其他Web API调用。

    但是我找不到关于如何执行此操作的简单指南...

    更新:

    从KEYCLOAK使用UI:

    到目前为止:

    我可以创建一个realm:

    例如:DemoRealm

    在realm下,我创建了客户端:

    例如:DemoClient

    在客户端下,我创建了用户:

    例如:DemoUser

    使用POSTMAN:

    我也能够成功使用获取令牌

    http://localhost:8080/auth/realms/DemoRelam/protocol/openid-connect/token

    POST:

    {

    "grant_type":"client_credentials",

    "username":"",

    "password":"",

    "client_secret":"",

    "client_id":"DemoClient"

    }

    作为回应,我得到了令牌。

    {

    "access_token":"eyJhbGciOiJSUzI1NiIsINVSHGhepnDu13SwRBL-v-y-04_6e6IJbMzreZwPI-epwdVPQe-ENhpvms2WdGM_DmgMLZ8YQFS4LDl9R7ZHT8AgXe-WCFV6OFkA7zvdeFwQ4kVVZE0HlNgHgoi4DrgMfwwz_ku1yJNJP3ztTY1nEqmA",

    "expires_in": 300,

    "refresh_expires_in": 1800,

    "refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJRRnB5YlloMGVEektIdlhOb3JvaFUxdlRvWVdjdP3vbfvk7O0zvppK9N4-oaUqZSr0smHv5LkuLDQYdPuxA",

    "token_type":"bearer",

    "not-before-policy": 0,

    "session_state":"bb1c586a-e880-4b96-ac16-30e42c0f46dc"

    }

    此外,我深入研究了更多细节,并找到了此API指南:

    http://www.keycloak.org/docs-api/3.0/rest-api/index.html#_users_resource

    在本指南中,提到了我可以使用

    获取用户返回用户列表,根据查询参数进行过滤

    GET /admin/realms/{realm}/users

    但是,当我使用POSTMAN获取用户时,我得到403错误代码。我正在传递与在上一步中获得的身份验证相同的令牌。

    http://localhost:8080/auth/admin/realms/DemoRelam/users

    谁能指导我?

    您是说您刚刚安装了密钥斗篷并在未将其与我们的应用程序集成时运行它吗?如果您在Internet和Keycloak文档中看到太多可用的内容,如何将Keycloak与Web应用程序集成

    @SubodhJoshi我不想将keycloak与Web应用程序集成。我要在relam下传递用户/客户端并获得令牌,然后将此令牌传递给其他一些api再次验证该令牌

    使用ui可能无法使用keycloak rest api或admin-cli。

    @SubodhJoshi我已经更新了我的问题,直到现在您能帮助我。

    我认为这会对您有所帮助。已经获得了访问令牌,但不知道如何使用邮递员对此进行测试。 >您只需要在Postman中添加authrisation标头,已经获得了访问令牌,但是不知道如何使用Postman进行测试。 >您只需要在Postman中添加授权标头,stackoverflow.com/questions/24709944/我发现这对您是否有帮助,否则我只能在早上用手机记录时才写正确的答案。

    @SubodhJoshi非常感谢您,我将发布其是否有效。现在还有一个问题,我可以在客户端级别获取令牌,是否可以在用户级别获取令牌

    @SubodhJoshi我认为它正在认证,所以我没有得到401,而是得到了403错误代码。似乎是一些授权错误。我不知道我为什么要这个

    禁止的403告诉我们,我们无权查看所请求的资源,这主要是由于允许。在许多情况下,如果您使用https且未将证书导入客户端,则会抛出403禁止错误。

    看到这一定会对您有所帮助。 stackoverflow.com/q/46470477/476828

    @SubodhJoshi非常感谢您的工作。

    @SubodhJoshi现在还有一个问题,我正在客户端级别获得令牌,是否可以在用户级别获得令牌

    在这里为您服务。出于多种原因,通过REST端点为普通用户获取令牌不是正确的方法。它不是SSO,安全性较差,它向应用程序等公开了身份验证详细信息和凭据。

    @SubodhJoshi所以你的意思是说在一个客户端下会有一组用户,然后我们需要获取该客户端的令牌而不是单个用户。如果我错了,请纠正我。

    每个领域可能包含或可能不包含一个以上的客户端。如果您要检查主领域,则默认情况下它具有多个客户端,并且领域仅包含一个以上的用户/组等信息。

    您有两种选择:您可以代表某个用户行事(如Adnan Khan指出的那样),或为此创建专用的客户。

    代表用户

    1)创建一个机密客户(我想您已经有一个)

    2)创建一个用户并为其分配适当的角色: realm-management组中的view-users

    3)获取令牌(我正在使用curl和jq):

    KCHOST=https://yourkeycloak.com

    REALM=realm

    CLIENT_ID=confidential-client

    CLIENT_SECRET=xxxxxxx-yyyyyy-zzzzzzzz

    UNAME=user

    PASSWORD=passwd

    ACCESS_TOKEN=`curl \

    -d"client_id=$CLIENT_ID" -d"client_secret=$CLIENT_SECRET" \

    -d"username=$UNAME" -d"password=$PASSWORD" \

    -d"grant_type=password" \

    "$KCHOST/auth/realms/$REALM/protocol/openid-connect/token"  | jq -r '.access_token'`

    4)最后调用Admin REST API用户终结点:

    curl -X GET -H"Authorization: Bearer $ACCESS_TOKEN" $KCHOST/auth/admin/realms/$REALM/users | jq

    代表客户(服务帐户)

    1)创建一个机密客户端,并确保将"启用服务帐户"设置切换为On

    2)转到Service account roles tab并为此客户端选择适当的角色,例如realm-management组中的realm-admin

    3)获取访问令牌

    KCHOST=https://yourkeycloak.com

    REALM=realm

    CLIENT_ID=protector-of-the-realm

    CLIENT_SECRET=xxxxxxx-yyyyyyyy-zzzzzzzzz

    ACCESS_TOKEN=`curl \

    -d"client_id=$CLIENT_ID" -d"client_secret=$CLIENT_SECRET" \

    -d"grant_type=client_credentials" \

    "$KCHOST/auth/realms/$REALM/protocol/openid-connect/token"  | jq -r '.access_token'`

    4)调用REST API端点:

    curl -X GET -H"Authorization: Bearer $ACCESS_TOKEN" $KCHOST/auth/admin/realms/$REALM/users | jq

    P.S. For debugging I have just written a CLI tool called brauzie that

    would help you fetch and analyse your JWT tokens (scopes, roles, etc.). It could

    be used for both public and confidential clients. You could as well

    use Postman and https://jwt.io

    HTH :)

    keycloak中的用户是特定于领域的,并非每个用户都可以访问它们。在管理控制台中为用户分配特定角色后,您将能够通过admin API获得所有用户。简单地做

    在管理控制台中,转到

    Users > myuser > Role Mappings > Client roles > realm-management

    为用户分配两个角色manage-users或view-users中的任何一个。

    生成一个新令牌。

    使用新令牌命中API

    您将拥有您的用户

    附言:我认为您应该将问题标题更新为更相关的内容。

    展开全文
  • 前一篇拙文是利用了Jmeter来测试带有CSRF令牌验证的WebAPI;最近几天趁着项目不忙,练习了用编码的方式实现。有了之前Jmeter脚本的基础,基本上难点也就在两个地方:获取CSRF令牌、Cookie的传递。首先添加依赖,在...

    前一篇拙文是利用了Jmeter来测试带有CSRF令牌验证的Web API;最近几天趁着项目不忙,练习了用编码的方式实现。

    有了之前Jmeter脚本的基础,基本上难点也就在两个地方:获取CSRF令牌、Cookie的传递。

    首先添加依赖,在POM.xml中添加以下内容:

    org.apache.httpcomponents

    httpclient

    4.5.6

    org.jsoup

    jsoup

    1.11.3

    解释作用:

    - httpClient:用来创建httpClient、管理Get和Post的方法、获取请求报文头、应答报文内容、管理CookieStore等等;

    - jsoup:用来解析应答报文,获得CSRF令牌的值。

    创建一个Web API测试类:

    public classLoginEHR {private final static String EHR_ADDRESS = "http://ourTestEHRServer:8083";static BasicCookieStore cookieStore = newBasicCookieStore();static CloseableHttpClient httpClient =HttpClients.custom().setDefaultCookieStore(cookieStore).build();

    我选择了CookieStore的方式管理会话;HttpClient现在还有另一种Context的方式实现会话持久,以后再做深入研究。

    先写一个打印应答报文的方法,并不做什么处理,纯打印;根据实际需要调用或者注释:

    public classLoginEHR {

    private static voidprintResponse(HttpResponse httpResponse)

    throwsParseException, IOException {//获取响应消息实体

    HttpEntity entity =httpResponse.getEntity();//响应状态

    System.out.println("--------Status: " +httpResponse.getStatusLine());

    System.out.println("--------Headers: ");

    HeaderIterator iterator=httpResponse.headerIterator();while(iterator.hasNext()) {

    System.out.println("\t" +iterator.next());

    }//判断响应实体是否为空

    if (entity != null) {

    String responseString=EntityUtils.toString(entity);

    System.out.println("--------Response length: " +responseString.length());

    System.out.println("--------Response content: "

    + responseString.replace("\r\n", ""));

    }

    }

    现在开始写测试方法,虽然篇幅较长,仍然写在main()方法里,便于展示:

    public classLoginEHR {private final static String EHR_ADDRESS = "http://ourTestEHRServer:8083";static BasicCookieStore cookieStore = newBasicCookieStore();static CloseableHttpClient httpClient =HttpClients.custom().setDefaultCookieStore(cookieStore).build();public static void main(String[] args) throwsException {

    String username= "00022222";

    String password= "abc123456";

    CloseableHttpResponse httpResponse= null;try{

    HttpGet httpGet= newHttpGet(EHR_ADDRESS);

    httpResponse=httpClient.execute(httpGet);

    System.out.println("--------Cookie store for the 1st GET: " +cookieStore.getCookies());//唯一的作用是打印应答报文,没有任何处理;实际测试时,可以不执行//printResponse(httpResponse);//取出第一次请求时,服务器端返回的JSESSIONID;//实际上此处只是取出JSESSIONID用作打印;cookieStore自动保存了本次会话的Cookie信息//List cookies = cookieStore.getCookies();//String cookie = cookies.toString();//String sessionID = cookie.substring("[[version: 0][name: JSESSIONID][value: ".length(),//cookie.indexOf("][domain"));//System.out.println("--------The current JSESSIONID is: " + sessionID);httpClient.close();

    }catch(Exception ex) {

    ex.printStackTrace();

    }

    }

    private static void printResponse(HttpResponse httpResponse)

    throws ParseException, IOException { ...... }

    根据之前Jmeter测试脚本的经验,先发送一次Get请求,从应答报文中得到CSRF令牌和JSESSIONID。

    大家注意我注释掉的那几行打印JSESSIONID的代码,之前在没有引入CookieStore之前,我想的是自己写一个新的Cookie,并把它赋给后面几次请求。

    当使用CookieStore之后,就不需要自己封装Cookie、以及添加到Request的Header了,这过程会自动完成。没有删掉也是为了需要的时候打印。

    交代完Cookie之后,该轮到处理CSRF令牌了。如果打印出第一次Get的应答,我们能看到令牌的格式是如下呈现的:

    84249e8a44c2a1a2791b4cbae10ac625.png

    之前在Jmeter脚本中,我是添加了一个正则表达式提取器,把_csrf的content提取出来。

    现在我将用jsoup来解析和返回content的内容,代码如下:

    private static String getCsrfToken(HttpEntity responseEntity) throwsIOException{//获取网页内容,指定编码

    String web = EntityUtils.toString(responseEntity,"utf-8");

    Document doc=Jsoup.parse(web);//选择器,选取特征信息

    String token = doc.select("meta[name=_csrf]").get(0).attr("content");

    System.out.println("--------The current CSRF Token is: " +token);returntoken;

    }

    在main()中调用此方法:

    //利用Jsoup从应答报文中读取CSRF Token HttpEntity responseEntity =httpResponse.getEntity();String token= getCsrfToken(responseEntity);

    然后再封装POST的请求内容:

    //获取到CSRF Token后,用Post方式登录

    HttpPost httpPost = newHttpPost(EHR_ADDRESS);//拼接Post的消息体

    List nvps = new ArrayList();

    nvps.add(new BasicNameValuePair("username", username));

    nvps.add(new BasicNameValuePair("password", password));

    nvps.add(new BasicNameValuePair("_csrf", token));

    HttpEntity loginParams= new UrlEncodedFormEntity(nvps, "utf-8");

    httpPost.setEntity(loginParams);//第二次请求,带有CSRF Token

    httpResponse =httpClient.execute(httpPost);//System.out.println("--------Cookie store for the POST: " + cookieStore.getCookies());

    printResponse(httpResponse);

    然后。。。这里发生了一点小意外:

    按照设想,应该能跳转到登录成功、或者验证失败的页面;而Post方法执行后,从服务器返回的状态码是302,被跳转到另一个网址。

    如果放任不管,直接提交后面的业务查询,是不会得到成功的;执行的结果是又回到了登录页面。

    我在网上爬了一会,发现提问Post得到301、302的人还不在少数,说明这个坑还是给很多人造成了困扰。

    简单的说,如果得到了服务器重定向到新的地址,我们也要跟着执行一次新地址的访问;否则服务器会认为这次请求没有得到正确处理,即便我之后的请求带着全套的验证令牌和Cookie,也会被拦截在系统外。

    有了这个认识,下面我需要完成的就是对Code:302的处理;添加代码如下:

    //取POST方法返回的HTTP状态码;不出意外的话是302

    int code =httpResponse.getStatusLine().getStatusCode();if (code == 302) {

    Header header= httpResponse.getFirstHeader("location"); //跳转的目标地址是在 HTTP-HEAD 中的

    String newUri = header.getValue(); //这就是跳转后的地址,再向这个地址发出新申请,以便得到跳转后的信息是啥。//实际打印出来的是接口服务地址,不包括IP Address部分

    System.out.println("--------Redirect to new location: " +newUri);

    httpGet= new HttpGet(EHR_ADDRESS +newUri);

    httpResponse=httpClient.execute(httpGet);//printResponse(httpResponse);

    }

    这里需要注意的地方是跳转的location内容。在我这里,服务器给的只是一个单词【/work】,最好加一个打印的步骤。

    确认不是一个完整的URL之后,需要把链接拼完整,然后进行一次httpGet请求。

    这个httpGet执行之后,我可以确认已经登录成功(或者,又被送回登录页面,当然我这里是成功了)。

    接下来是提交一次业务查询的Get,确认能够在系统中进行业务操作:

    //请求一次绩效;确认登录成功

    String queryUrl = EHR_ADDRESS + "/emp/performance/mt/query";

    httpGet= newHttpGet(queryUrl);

    httpResponse=httpClient.execute(httpGet);

    System.out.println("--------Result of the Cardpunch Query: ");

    printResponse(httpResponse);

    最后确认查询的结果无误后,整个脚本完成;只需要修改最后的业务查询,就可以生成其他的测试脚本了。

    完整的源码如下:

    packagecom.jason.apitest;importorg.apache.http.Header;importorg.apache.http.HeaderIterator;importorg.apache.http.HttpEntity;importorg.apache.http.HttpResponse;importorg.apache.http.NameValuePair;importorg.apache.http.ParseException;importorg.apache.http.client.entity.UrlEncodedFormEntity;importorg.apache.http.client.methods.HttpGet;importorg.apache.http.client.methods.HttpPost;importorg.apache.http.impl.client.BasicCookieStore;importorg.apache.http.impl.client.CloseableHttpClient;importorg.apache.http.impl.client.HttpClients;importorg.apache.http.message.BasicNameValuePair;importorg.apache.http.protocol.HTTP;importorg.apache.http.util.EntityUtils;importorg.jsoup.Jsoup;importorg.jsoup.nodes.Document;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;public classLoginEHR {private final static String EHR_ADDRESS = "http://ourTestEHRServer:8083";static BasicCookieStore cookieStore = newBasicCookieStore();static CloseableHttpClient httpClient =HttpClients.custom().setDefaultCookieStore(cookieStore).build();public static void main(String[] args) throwsException {

    String username= "00022222";

    String password= "abc123456";

    HttpResponse httpResponse= null;try{

    HttpGet httpGet= newHttpGet(EHR_ADDRESS);

    httpResponse=httpClient.execute(httpGet);

    System.out.println("--------Cookie store for the 1st GET: " +cookieStore.getCookies());//唯一的作用是打印应答报文,没有任何处理;实际测试时,可以不执行//printResponse(httpResponse);//取出第一次请求时,服务器端返回的JSESSIONID;//实际上此处只是取出JSESSIONID用作打印;cookieStore自动保存了本次会话的Cookie信息//List cookies = cookieStore.getCookies();//String cookie = cookies.toString();//String sessionID = cookie.substring("[[version: 0][name: JSESSIONID][value: ".length(),//cookie.indexOf("][domain"));//System.out.println("--------The current JSESSIONID is: " + sessionID);//利用Jsoup从应答报文中读取CSRF Token

    HttpEntity responseEntity =httpResponse.getEntity();

    String token=getCsrfToken(responseEntity);//获取到CSRF Token后,用Post方式登录

    HttpPost httpPost = newHttpPost(EHR_ADDRESS);//拼接Post的消息体

    List nvps = new ArrayList();

    nvps.add(new BasicNameValuePair("username", username));

    nvps.add(new BasicNameValuePair("password", password));

    nvps.add(new BasicNameValuePair("_csrf", token));

    HttpEntity loginParams= new UrlEncodedFormEntity(nvps, "utf-8");

    httpPost.setEntity(loginParams);//第二次请求,带有CSRF Token

    httpResponse =httpClient.execute(httpPost);//System.out.println("--------Cookie store for the POST: " + cookieStore.getCookies());

    printResponse(httpResponse);//取POST方法返回的HTTP状态码;不出意外的话是302

    int code =httpResponse.getStatusLine().getStatusCode();if (code == 302) {

    Header header= httpResponse.getFirstHeader("location"); //跳转的目标地址是在 HTTP-HEAD 中的

    String newUri = header.getValue(); //这就是跳转后的地址,再向这个地址发出新申请,以便得到跳转后的信息是啥。//实际打印出来的是接口服务地址,不包括IP Address部分

    System.out.println("--------Redirect to new location: " +newUri);

    httpGet= new HttpGet(EHR_ADDRESS +newUri);

    httpResponse=httpClient.execute(httpGet);//printResponse(httpResponse);

    }//请求一次绩效;确认登录成功

    String queryUrl = EHR_ADDRESS + "/emp/performance/mt/query";

    httpGet= newHttpGet(queryUrl);

    httpResponse=httpClient.execute(httpGet);

    System.out.println("--------Result of the Cardpunch Query: ");

    printResponse(httpResponse);

    httpClient.close();

    }catch(Exception ex) {

    ex.printStackTrace();

    }

    }private static voidprintResponse(HttpResponse httpResponse)throwsParseException, IOException {//获取响应消息实体

    HttpEntity entity =httpResponse.getEntity();//响应状态

    System.out.println("--------Status: " +httpResponse.getStatusLine());

    System.out.println("--------Headers: ");

    HeaderIterator iterator=httpResponse.headerIterator();while(iterator.hasNext()) {

    System.out.println("\t" +iterator.next());

    }//判断响应实体是否为空

    if (entity != null) {

    String responseString=EntityUtils.toString(entity);

    System.out.println("--------Response length: " +responseString.length());

    System.out.println("--------Response content: "

    + responseString.replace("\r\n", ""));

    }

    }private static String getCsrfToken(HttpEntity responseEntity) throwsIOException{//获取网页内容,指定编码

    String web = EntityUtils.toString(responseEntity,"utf-8");

    Document doc=Jsoup.parse(web);//选择器,选取特征信息

    String token = doc.select("meta[name=_csrf]").get(0).attr("content");

    System.out.println("--------The current CSRF Token is: " +token);returntoken;

    }

    }

    补充:如果使用HttpClientContext方式来维持会话,与CookieStore很接近;直接帖上需要修改的部分内容:

    //创建httpClient和context

    static CloseableHttpClient httpClient =HttpClients.createDefault();static HttpClientContext context =HttpClientContext.create();

    // 下面的代码写在main()方法中

    CloseableHttpResponse httpResponse= null;//先发起一个Get请求,获取CSRF令牌和Cookie

    HttpGet httpGet = newHttpGet(EHR_ADDRESS);//保存context上下文

    httpResponse =httpClient.execute(httpGet, context);

    ...//处理完CSRF令牌后,准备发起POST请求

    HttpPost httpPost = newHttpPost(EHR_ADDRESS);

    ...//封装POST报文//发起POST请求

    httpResponse =httpClient.execute(httpPost, context);//处理HTTP 302和业务查询操作的GET,也要携带着context

    httpResponse = httpClient.execute(httpGet, context);

    展开全文
  • .create(Login::class.java) val refreshToken = "yourRefreshToken" val call = service?.refreshToken(refreshToken) call?.enqueue(object: Callback{ override fun onFailure(call: Call, t: Throwable) { print...
  • java 移动端接口令牌This article was originally published on Alibaba Cloud. Thank you for supporting the partners who make SitePoint possible. 本文最初发表在阿里云上 。 感谢您支持使SitePoint成为可能的...
  • 而常用的限流算法有令牌桶算法和漏桶算法,本篇介绍令牌桶算法令牌桶算法image.png原理如上图,系统以恒定速率不断产生令牌令牌桶有最大容量,超过最大容量则丢弃,同时用户请求接口,如果此时令牌桶中有令牌则能...
  • JWT 简单介绍全称 Json Web Token 致力于网络安全的一套Json开放标准主要流程将用户部分信息进行加密,生成一套令牌,存放于客户端客户端发起请求时,带入此令牌,交由服务端识别令牌真伪服务端校验完成,开放部分...
  • 令牌桶算法场景:秒杀(也可以用于平滑限流某个接口请求)import java.io.IOException;import java.nio.charset.Charset;import org.springframework.core.io.ClassPathResource;import com.google.common.io.Files;...
  • 0. 前言常见的限流算法有:令牌桶、漏桶。计数器也可以进行粗暴限流实现。1. 算法介绍1.1 令牌桶算法令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌令牌桶算法的描述如下:假设限制2r/s,则...
  • 1.策略接口package com.sme.services.validateIntface;.../**** @author injuer** @see 此接口可根据程序开发人员自定义令牌的编号算法* 和针对每个不同作用域关联的操作的信息处理**/public inte...
  • java token登录令牌

    2021-05-06 15:53:43
    token 值: 登录令牌.利用 token 值来判断用户的登录状态.类似于 MD5 加密之后的长字符串 用户登录成功之后,在后端(服务器端)会根据用户信息生成一个唯一的值.这个值就是 token 值 生成完之后将token值返回给前端,...
  • 业务背景系统需要对接某IM厂商rest接口,向客户端推送消息(以及其他IM业务)该厂商对rest接口调用有频率限制:总rest调用9000次/30s;消息推送600次/30s系统为分布式集群,需要控制整个分布式集群总的接口调用频率...
  • Java 接口限流

    千次阅读 2018-09-02 14:42:48
    当有访问者(针对于 IP)要访问接口时,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 当桶满时,新添加的令牌被丢弃或拒绝。   2、知识点 Springboot Guava -- RateLimi...
  • Java接口限流算法

    千次阅读 2018-07-20 08:39:55
    令牌桶算法是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌令牌桶算法的描述如下: * 假设限制2r/s,则按照500毫秒的固定速率往桶中添加令牌; * 桶中最多存放b个令牌,当桶满时,新添加的令牌被丢弃...
  • 接口限流算法:令牌

    2019-10-14 13:18:23
    接口限流算法:令牌桶 本文内容: 令牌桶算法原理 实现令牌桶算法 Guava中RateLimiter令牌桶的使用 限流算法的应用场景 令牌桶算法原理 ...简单说明:设定固定的速率往桶中放入令牌,如果...JAVA实现令牌桶算法 pa...
  • 工作中对外提供的API 接口设计都要考虑限流,如果不考虑限流,会成系统的连锁反应,轻者响应缓慢,重者系统宕机,整个业务线崩溃,如何应对这种情况呢,我们可以对请求进行引流或者直接拒绝等操作,保持系统的可用性...
  • Java微信接口调用

    2015-12-25 16:32:29
    主要是Java调用微信公众号开发接口代码。包括: 1、创建菜单接口 2、根据code获取openid 3、获取令牌token 4、根据openid获取用户基本信息 5、根据openid发送模板/客服消息 6、微信支付:微信获取统一下单包/接收...
  • 为什么要限流在电商中经常有秒杀的场景,就是...具体样例我这里先自定义作用于方法上的限流注解,然后用aop切面去拦截包含有自定义限流注解的接口,再结合谷歌开源工具包com.google.concurrent.RateLimiter类(令牌桶...
  • 服务限流 -- 自定义注解基于RateLimiter实现接口限流令牌桶限流算法图片来自网上令牌桶会以一个恒定的速率向固定容量大小桶中放入令牌,当有浏览来时取走一个或者多个令牌,当发生高并发情况下拿到令牌的执行业务逻辑...
  • java天气接口代码

    2018-03-23 13:39:44
    url:https://www.juhe.cn/docs/api/id/39/aid/87step2:每个接口都需要传入一个参数key,相当于用户的令牌,所以第一步你需要申请一个keystep3:看文档!!!学过java的同学们都知道,当我们对一个类或者方法不明白其意图和...
  • package org.test.gateway.filter....import java.time.Duration;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.spr...
  • java对外接口开发实例

    2021-03-17 10:32:45
    使用授权码_secret,从服务代理端获取令牌。 使用令牌,对服务请求者身份标识(_orgid)、服务标识(_servicecode)和请求时间(_rtime)进行令牌密钥计算,得到令牌密钥信息_token。 将服务请求者身份标识(_...
  • RateLimiterRateLimiter基于令牌桶算法,有一个令牌桶,单位时间内令牌会以恒定的数量(即令牌的加入速度)加入到令牌桶中,所有请求都需要获取令牌才可正常访问。当令牌桶中没有令牌可取的时候,则拒绝请求。定义一个...
  • 对于百度搜索不需要身份信息,直接运行框架可以正常测试百度搜索,但是对于企业微信而言其中的操作需要令牌信息(出于安全考虑,博主未将自己的令牌放置其中),所以如要测试通过企业微信的接口,需要大家注册一下...
  • 一、前言通过gujava软件包的com.google.common.util.concurrent.RateLimiter的实现通过令牌桶的方式在高并发应用接口场景下进行系统限流,其他限流方式还有漏桶、滑动窗口、计数器等。二、源码示例下面...
  • 转自:博客园 作者:巨人大哥www.cnblogs.com/jurendage/p/12653865.html一:token 简介Token:访问令牌access token, 用于接口中, 用于标识接口调用者的身份、凭证,减少用户名和密码的传输次数。一般情况下客户端...
  • 如何对同一IP访问同一接口进行每秒、每分钟、每小时频率限制话不多说,直接开干,首先写一个注解类```import ```使用注解的原因是:我们使用拦截器在请求处理之前,检查某个请求接口是否有该注解,如果有该注解,...
  • package ... import java.util.HashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.b...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 213
精华内容 85
热门标签
关键字:

java接口令牌

java 订阅