谷粒学院——Day12【整合阿里云短信服务、首页登录和注册】
❤ 作者主页:欢迎来到我的技术博客❀ 个人介绍:大家好,本人热衷于Java后端开发,欢迎来交流学习哦!( ̄▽ ̄)~* 如果文章对您有帮助,记得关注、点赞、收藏、评论⭐️⭐️⭐️ 您的支持将是我创作的动力,让我们一起加油进步吧!!!用户登录业务介绍一、单一服务器模式早期单一服务器,用户认证。缺点:单点性能压力,无法扩展。二、SSO(single sign on)模式分布式,SSO(single sign on)模式 优点:用户身份信息独立管理,更好的分布式管理。可以自己扩展安全策略。缺点:认证服务器访问压力较大。三、Token模式token是按照一定规则生成字符串,包含用户信息。业务流程图{用户访问业务时,必须登录的流程} 优点:无状态: token无状态,session有状态的。基于标准化: 你的API可以采用标准化的 JSON Web Token (JWT)缺点:占用带宽。无法在服务器端销毁。注:基于微服务开发,选择token的形式相对较多,因此使用token作为用户认证的标准。整合JWTtoken是按照一定的规则生成字符串,包含用户信息。JWT就是我们规定好了规则,使用JWT规则可以生成字符串,包含用户信息。一、使用JWT进行跨域身份验证1. 传统用户身份验证 Internet服务无法与用户身份验证分开。一般过程如下:用户向服务器发送用户名和密码。验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。服务器向用户返回session_id,session信息都会写入到用户的Cookie。用户的每个后续请求都将通过在Cookie中取出session_id传给服务器。服务器收到session_id并对比之前保存的数据,确认用户的身份。这种模式最大的问题是,没有分布式架构,无法支持横向扩展。2. 解决方案session广播。将透明令牌存入cookie,将用户身份信息存入redis。另外一种灵活的解决方案:使用自包含令牌,通过客户端保存数据,而服务器不保存会话数据。 JWT是这种解决方案的代表。 二、JWT令牌1. 访问令牌的类型 2. JWT的组成典型的,一个JWT看起来如下图: 该对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名哈希。JWT头JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。{
"alg": "HS256",
"typ": "JWT"
}在上面的代码中,alg属性 表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性 表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。有效载荷有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择。iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT除以上默认字段外,我们还可以自定义私有字段,如下例:{
"sub": "1234567890",
"name": "Helen",
"admin": true
}请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。签名哈希签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。Base64URL算法如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/"和"=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"="去掉,"+"用"-"替换,"/"用"_"替换,这就是Base64URL算法。 3. JWT的原则JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,如下所示。{
"sub": "1234567890",
"name": "Helen",
"admin": true
}之后,当用户与服务器通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。 4. JWT的用法客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。 5. JWT问题和趋势JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库存储在客户端,不占用服务端的内存资源JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。当JWT未加密时,一些私密数据无法通过JWT传输。JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。三、整合JWT令牌1. 在common_utils模块中添加jwt工具依赖<dependencies>
<!-- JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
2. 创建JWT工具类public class JwtUtils {
//token过期时间
public static final long EXPIRE = 1000 * 60 * 60 * 24;
//秘钥,每个公司生成规则不一样
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
//生成token字符串方法
public static String getJwtToken(String id, String nickname) {
String JwtToken = Jwts.builder()
//设置jwt头信息,红色部分,内容固定,不需要改
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) //设置过期时间
//设置token主体部分,存储用户信息,可设置多个值
.claim("id", id)
.claim("nickname", nickname)
//设置签名哈希(防伪标志)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
/**
* 判断token是否存在与有效
* @param jwtToken
* @return
*/
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 判断token是否存在与有效
* @param request
* @return
*/
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
//根据设置的防伪码解析token
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 根据token获取会员id
* @param request
* @return
*/
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
//根据设置的防伪码解析token,获取对象
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
//获取token有效载荷【用户信息】
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
} 整合阿里云短信服务一、新建短信微服务1. 在service模块下创建子模块service-msm 2. 创建controller和service代码\ 3. 配置application.properties# 服务端口
server.port=8006
# 服务名
spring.application.name=service-msm
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=abc123
# redis配置
spring.redis.host=192.168.152.128
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空闲
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl4. 创建启动类@ComponentScan({"com.atguigu"})
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //取消数据源自动配置
public class MsmApplication {
public static void main(String[] args) {
SpringApplication.run(MsmApplication.class, args);
}
}二、阿里云短信服务官方文档:https://help.aliyun.com/product/44282.html?spm=5176.10629532.0.0.38311cbeYzBm73三、编写发送短信接口1. 在service-msm的pom中引入依赖 <dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
</dependencies>2. 编写controller@RestController
@RequestMapping("/edumsm/msm")
@CrossOrigin
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
//发送短信的方法
@GetMapping("send/{phone}")
public R sendMsm(@PathVariable String phone) {
// 从redis获取验证码,如果能获取,直接返回
String code = redisTemplate.opsForValue().get(phone);
if (!StringUtils.isEmpty(code)) {
return R.ok();
}
// 获取不到就阿里云发送
// 生成随机数,传递给阿里云进行发送
code = RandomUtil.getFourBitRandom();
Map<String, Object> param = new HashMap<>();
param.put("code", code);
// 调用service发送短信的方法
boolean isSend = msmService.send(param, phone);
if (isSend) {
// 如果发送成功,把发送成功的code验证码保存到redis中,并设置有效时间,设置5分钟内有效
redisTemplate.opsForValue().set(phone, code, 5, TimeUnit.MINUTES);
return R.ok();
} else {
return R.error().message("短信发送失败");
}
}
}3. 编写service@Service
public class MsmServiceImpl implements MsmService {
//发送短信的方法
@Override
public boolean send(Map<String, Object> param, String phone) {
if (StringUtils.isEmpty(phone)) return false;
//参数1:地域节点
//参数2:AccessKey ID
//参数3:AccessKey Secret
DefaultProfile profile = DefaultProfile.getProfile("default", "LTAI5tHxdu7qnz8kEVAd7fYc", "vlhzalqMhVeQcZVvHn4hGudffEL7EC");
DefaultAcsClient client = new DefaultAcsClient(profile);
// 设置相关固定的参数
CommonRequest request = new CommonRequest();
//request.setProtocol(ProtocolType.HTTPS);
request.setSysMethod(MethodType.POST); //提交方式,默认不能改
request.setSysDomain("dysmsapi.aliyuncs.com"); //请求阿里云哪里,默认不能改
request.setSysVersion("2017-05-25"); //版本号
request.setSysAction("SendSms"); //请求哪个方法
// 设置发送相关的参数
request.putQueryParameter("PhoneNumbers",phone); //设置要发送的【手机号】
request.putQueryParameter("SignName","在线教育网站"); //申请阿里云短信服务的【签名名称】
request.putQueryParameter("TemplateCode","SMS_154950909"); //申请阿里云短信服务的【模版中的 模版CODE】
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); //要求传递的code验证码为jason格式,可以使用JSONObject.toJSONString()将map转为json格式
//最终发送
try {
CommonResponse response = client.getCommonResponse(request);
return response.getHttpResponse().isSuccess();
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}
用户登录注册接口【后端】一、新建用户微服务1. 在service模块下创建子模块service-ucenter 2. 使用代码生成器生成代码创建ucenter_member表# Host: 47.93.118.241:33306 (Version 5.7.21)
# Date: 2019-11-15 11:58:11
# Generator: MySQL-Front 6.1 (Build 1.26)
#
# Structure for table "ucenter_member"
#
CREATE TABLE `ucenter_member` (
`id` CHAR(19) NOT NULL COMMENT '会员id',
`openid` VARCHAR(128) DEFAULT NULL COMMENT '微信openid',
`mobile` VARCHAR(11) DEFAULT '' COMMENT '手机号',
`password` VARCHAR(255) DEFAULT NULL COMMENT '密码',
`nickname` VARCHAR(50) DEFAULT NULL COMMENT '昵称',
`sex` TINYINT(2) UNSIGNED DEFAULT NULL COMMENT '性别 1 女,2 男',
`age` TINYINT(3) UNSIGNED DEFAULT NULL COMMENT '年龄',
`avatar` VARCHAR(255) DEFAULT NULL COMMENT '用户头像',
`sign` VARCHAR(100) DEFAULT NULL COMMENT '用户签名',
`is_disabled` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用, 0(false)未禁用',
`is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` DATETIME NOT NULL COMMENT '创建时间',
`gmt_modified` DATETIME NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
#
# Data for table "ucenter_member"
#
INSERT INTO `ucenter_member` VALUES ('1',NULL,'13700000001','96e79218965eb72c92a549dd5a330112','小三123',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-01 12:11:33','2019-11-08 11:56:01'),('1080736474267144193',NULL,'13700000011','96e79218965eb72c92a549dd5a330112','用户XJtDfaYeKk',1,19,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:12:45','2019-01-02 12:12:56'),('1080736474355224577',NULL,'13700000002','96e79218965eb72c92a549dd5a330112','用户wUrNkzAPrc',1,27,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:13:56','2019-01-02 12:14:07'),('1086387099449442306',NULL,'13520191388','96e79218965eb72c92a549dd5a330112','用户XTMUeHDAoj',2,20,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099520745473',NULL,'13520191389','96e79218965eb72c92a549dd5a330112','用户vSdKeDlimn',1,21,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099608825858',NULL,'13520191381','96e79218965eb72c92a549dd5a330112','用户EoyWUVXQoP',1,18,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099701100545',NULL,'13520191382','96e79218965eb72c92a549dd5a330112','用户LcAYbxLNdN',2,24,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099776598018',NULL,'13520191383','96e79218965eb72c92a549dd5a330112','用户dZdjcgltnk',2,25,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099852095490',NULL,'13520191384','96e79218965eb72c92a549dd5a330112','用户wNHGHlxUwX',2,23,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1106746895272849410','o1R-t5u2TfEVeVjO9CPGdHPNw-to',NULL,NULL,'檀梵\'',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/zZfLXcetf2Rpsibq6HbPUWKgWSJHtha9y1XBeaqluPUs6BYicW1FJaVqj7U3ozHd3iaodGKJOvY2PvqYTuCKwpyfQ/132',NULL,0,0,'2019-03-16 10:39:57','2019-03-16 10:39:57'),('1106822699956654081',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:41:10','2019-03-16 15:41:10'),('1106823035660357634','o1R-t5i4gENwHYRb5lVFy98Z0bdk',NULL,NULL,'GaoSir',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJI53RcCuc1no02os6ZrattWGiazlPnicoZQ59zkS7phNdLEWUPDk8fzoxibAnXV1Sbx0trqXEsGhXPw/132',NULL,0,0,'2019-03-16 15:42:30','2019-03-16 15:42:30'),('1106823041599492098',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:42:32','2019-03-16 15:42:32'),('1106823115788341250','o1R-t5l_3rnbZbn4jWwFdy6Gk6cg',NULL,NULL,'换个网名哇、',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/jJHyeM0EN2jhB70LntI3k8fEKe7W6CwykrKMgDJM4VZqCpcxibVibX397p0vmbKURGkLS4jxjGB0GpZfxCicgt07w/132',NULL,0,0,'2019-03-16 15:42:49','2019-03-16 15:42:49'),('1106826046730227714','o1R-t5gyxumyBqt0CWcnh0S6Ya1g',NULL,NULL,'我是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKDRfib8wy7A2ltERKh4VygxdjVC1x5OaOb1t9hot4JNt5agwaVLdJLcD9vJCNcxkvQnlvLYIPfrZw/132',NULL,0,0,'2019-03-16 15:54:28','2019-03-16 15:54:28'),('1106828185829490690','o1R-t5nNlou5lRwBVgGNJFm4rbc4',NULL,NULL,' 虎头',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKxCqRzuYWQmpwiaqQEjNxbC7WicebicXQusU306jgmfoOzUcFg1qaDq5BStiblwBjw5dUOblQ2gUicQOQ/132',NULL,0,0,'2019-03-16 16:02:58','2019-03-16 16:02:58'),('1106830599651442689','o1R-t5hZHQB1cbX7HZJsiM727_SA',NULL,NULL,'是吴啊',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJ9CsqApybcs7f3Dyib9IxIh0sBqJb7LicbjU4WticJFF0PVwFvHgtbFdBwfmk3H2t3NyqmEmVx17tRA/132',NULL,0,0,'2019-03-16 16:12:34','2019-03-16 16:12:34'),('1106830976199278593','o1R-t5meKOoyEJ3-IhWRCBKFcvzU',NULL,NULL,'我才是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83epMicP9UT6mVjYWdno0OJZkOXiajG0sllJTbGJ9DYiceej2XvbDSGCK8LCF7jv1PuG2uoYlePWic9XO8A/132',NULL,0,0,'2019-03-16 16:14:03','2019-03-16 16:14:03'),('1106831936900415490','o1R-t5jXYSWakGtnUBnKbfVT5Iok',NULL,NULL,'文若姬',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/3HEmJwpSzguqqAyzmBwqT6aicIanswZibEOicQInQJI3ZY1qmu59icJC6N7SahKqWYv24GvX5KH2fibwt0mPWcTJ3fg/132',NULL,0,0,'2019-03-16 16:17:52','2019-03-16 16:17:52'),('1106832491064442882','o1R-t5sud081Qsa2Vb2xSKgGnf_g',NULL,NULL,'Peanut',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 16:20:04','2019-03-16 16:20:04'),('1106833021442510849','o1R-t5lsGc3I8P5bDpHj7m_AIRvQ',NULL,NULL,'食物链终结者',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/MQ7qUmCprK9am16M1Ia1Cs3RK0qiarRrl9y8gsssBjIZeS2GwKSrnq7ZYhmrzuzDwBxSMMAofrXeLic9IBlW4M3Q/132',NULL,0,0,'2019-03-16 16:22:11','2019-03-16 16:22:11'),('1191600824445046786',NULL,'15210078344','96e79218965eb72c92a549dd5a330112','IT妖姬',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 14:19:10','2019-11-08 18:04:43'),('1191616288114163713',NULL,'17866603606','96e79218965eb72c92a549dd5a330112','xiaowu',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 15:20:37','2019-11-05 15:20:37'),('1195187659054329857',NULL,'15010546384','96e79218965eb72c92a549dd5a330112','qy',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-15 11:51:58','2019-11-15 11:51:58');
生成代码public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("D:\\IDEA\\guli_parent\\service\\service_ucenter" + "/src/main/java"); //输出目录
gc.setAuthor("jyu_zwy"); //作者名
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guli?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("abc123");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
//生成包:com.atguigu.eduservice
pc.setModuleName("educenter"); //模块名
pc.setParent("com.atguigu");
//生成包:com.atguigu.controller
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("ucenter_member");//根据数据库哪张表生成,有多张表就加逗号继续填写
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
} 3. 配置application.properties# 服务端口
server.port=8006
# 服务名
spring.application.name=service-ucenter
# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
# redis
spring.redis.host=192.168.152.128
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0
#最小空闲
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/atguigu/educenter/mapper/xml/*.xml
#mybatis日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
4. 创建启动类@ComponentScan({"com.atguigu"})
@SpringBootApplication
@MapperScan("com.atguigu.educenter.mapper")
public class UcenterApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterApplication.class, args);
}
} 二、创建登录和注册接口1. 创建LoginVo和RegisterVo用于数据封装LoginVo@Data
@ApiModel(value="登录对象", description="登录对象")
public class LoginVo {
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
}RegisterVo@Data
@ApiModel(value="注册对象", description="注册对象")
public class RegisterVo {
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
}
2. 创建controller编写登录和注册方法UcenterMemberController@RestController
@CrossOrigin
@RequestMapping("/educenter/member")
public class UcenterMemberController {
@Autowired
private UcenterMemberService memberService;
// 登录
@PostMapping("login")
public R loginUser(@RequestBody LoginVo loginVo) {
// 调用service方法实现登录
// 返回token值,使用jwt生成
String token = memberService.login(loginVo);
return R.ok().data("token", token);
}
// 注册
@PostMapping("register")
public R registerUser(@RequestBody RegisterVo registerVo) {
memberService.register(registerVo);
return R.ok();
}
}
3. 创建service接口和实现类UcenterMemberServicepublic interface UcenterMemberService extends IService<UcenterMember> {
// 登录的方法
String login(LoginVo loginVo);
// 注册的方法
void register(RegisterVo registerVo);
}UcenterMemberServiceImpl@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
// 登录的方法
@Override
public String login(LoginVo loginVo) {
// 获取登录手机号和密码
String mobile = loginVo.getMobile();
String password = loginVo.getPassword();
// 手机号和密码非空判断
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(mobile)){
throw new GuliException(20001,"手机号或密码为空");
}
// 判断手机号是否正确
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
UcenterMember mobileMember = baseMapper.selectOne(wrapper);
// 判断查询对象是否为空
if (mobileMember == null) { // 没有这个手机号
throw new GuliException(20001, "手机号不存在");
}
// 判断密码是否正确
// MD5加密是不可逆性的,不能解密,只能加密
//将获取到的密码经过MD5加密与数据库比较
if (!MD5.encrypt(password).equals(mobileMember.getPassword())) {
throw new GuliException(20001, "密码错误");
}
// 判断是否被禁用
if (mobileMember.getIsDisabled()) {
throw new GuliException(20001, "用户被禁用,登录失败");
}
// 登录成功
// 生成token字符串, 使用jwt工具类
String jwtToken = JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
return jwtToken;
}
// 注册的方法
@Override
public void register(RegisterVo registerVo) {
// 获取注册的信息
String nickname = registerVo.getNickname(); //昵称
String code = registerVo.getCode(); //验证码
String mobile = registerVo.getMobile(); //手机号
String password = registerVo.getPassword(); //密码
// 非空判断
if (StringUtils.isEmpty(nickname) ||StringUtils.isEmpty(code)
||StringUtils.isEmpty(mobile) ||StringUtils.isEmpty(password)) {
throw new GuliException(20001, "注册的数据有空值,注册失败");
}
// 判断验证码
// 获取redis验证码
// String redisCode = redisTemplate.opsForValue().get(mobile);
// if (!code.equals(redisCode)) {
// throw new GuliException(20001, "验证码错误。注册失败");
// }
// 直接写死一个验证码
if (!code.equals("1234")) {
throw new GuliException(20001, "验证码错误。注册失败");
}
// 判断手机号是否重复,表里面存在相同手机号则不进行添加
QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
wrapper.eq("mobile", mobile);
Integer count = baseMapper.selectCount(wrapper);
if (count > 0) {
throw new GuliException(20001, "手机号重复,注册失败");
}
// 注册成功
// 数据添加到数据库中
UcenterMember member = new UcenterMember();
member.setPassword(MD5.encrypt(password));//密码加密
member.setMobile(mobile);
member.setNickname(nickname);
member.setIsDisabled(false);//用户不禁用
member.setAvatar("https://zwy-edu.oss-cn-hangzhou.aliyuncs.com/2022/11/1638921791030300677.jpg");
baseMapper.insert(member);
}
}4. 测试登录 注册 三、创建接口根据token获取用户信息在 MemberApiController 中创建方法:// 根据token获取用户信息
@GetMapping("getMemberInfo")
public R getMemberInfo(HttpServletRequest request) {
// 调用jwt工具的方法。根据request对象获取头信息,返回用户id
String memberId = JwtUtils.getMemberIdByJwtToken(request);
// 查询数据库根据用户id获取用户信息
UcenterMember member = memberService.getById(memberId);
return R.ok().data("userInfo", member);
} 用户登录注册【前端】一、在nuxt环境中安装插件1.安装element-ui 和 vue-qriouslynpm install element-ui
npm install vue-qriously2. 修改配置文件 nuxt-swiper-plugin.jsimport Vue from 'vue'
import VueAwesomeSwiper from '../node_modules/vue-awesome-swiper/dist/ssr'
import VueQriously from 'vue-qriously'
import ElementUI from 'element-ui' //element-ui的全部组件
import 'element-ui/lib/theme-chalk/index.css'//element-ui的css
Vue.use(ElementUI) //使用elementUI
Vue.use(VueQriously)
Vue.use(VueAwesomeSwiper)二、用户注册功能前端整合1. 在api文件夹中创建注册的js文件,定义接口register.jsimport request from '@/utils/request'
export default{
//根据手机号码发送短信
sendCode(phone){
return request({
url: `/edumsm/msm/send/${phone}`,
method: 'get'
})
},
// 注册的方法
registerMember(formItem){
return request({
url: `/educenter/member/register`,
method: 'post',
data: formItem
})
},
}2. 在pages文件夹中创建注册页面,调用方法在layouts创建布局页面sign.vue<template>
<div class="sign">
<!--标题-->
<div class="logo">
<img src="~/assets/img/logo.png" alt="logo" />
</div>
<!--表单-->
<nuxt />
</div>
</template>创建注册页面register.vue<template>
<div class="main">
<div class="title">
<a href="/login">登录</a>
<span>·</span>
<a class="active" href="/register">注册</a>
</div>
<div class="sign-up-container">
<el-form ref="userForm" :model="params">
<el-form-item
class="input-prepend restyle"
prop="nickname"
:rules="[
{
required: true,
message: '请输入你的昵称',
trigger: 'blur',
},
]"
>
<div>
<el-input
type="text"
placeholder="你的昵称"
v-model="params.nickname"
/>
<i class="iconfont icon-user" />
</div>
</el-form-item>
<el-form-item
class="input-prepend restyle no-radius"
prop="mobile"
:rules="[
{ required: true, message: '请输入手机号码', trigger: 'blur' },
{ validator: checkPhone, trigger: 'blur' },
]"
>
<div>
<el-input
type="text"
placeholder="手机号"
v-model="params.mobile"
/>
<i class="iconfont icon-phone" />
</div>
</el-form-item>
<el-form-item
class="input-prepend restyle no-radius"
prop="code"
:rules="[
{ required: true, message: '请输入验证码', trigger: 'blur' },
]"
>
<div
style="width: 100%; display: block; float: left; position: relative"
>
<el-input type="text" placeholder="验证码" v-model="params.code" />
<i class="iconfont icon-phone" />
</div>
<div
class="btn"
style="position: absolute; right: 0; top: 6px; width: 40%"
>
<a
href="javascript:"
type="button"
@click="getCodeFun()"
:value="codeTest"
style="border: none; background-color: none"
>{{ codeTest }}</a
>
</div>
</el-form-item>
<el-form-item
class="input-prepend"
prop="password"
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
>
<div>
<el-input
type="password"
placeholder="设置密码"
v-model="params.password"
/>
<i class="iconfont icon-password" />
</div>
</el-form-item>
<div class="btn">
<input
type="button"
class="sign-up-button"
value="注册"
@click="submitRegister()"
/>
</div>
<p class="sign-up-msg">
点击 “注册” 即表示您同意并愿意遵守简书
<br />
<a target="_blank" href="http://www.jianshu.com/p/c44d171298ce"
>用户协 议</a
>
和
<a target="_blank" href="http://www.jianshu.com/p/2ov8x3">隐私政策</a>
。
</p>
</el-form>
<!-- 更多注册方式 -->
<div class="more-sign">
<h6>社交帐号直接注册</h6>
<ul>
<li>
<a
id="weixin"
class="weixin"
target="_blank"
href="http://huaan.free.idcfengye.com/api/ucenter/wx/login"
><i class="iconfont icon-weixin"
/></a>
</li>
<li>
<a id="qq" class="qq" target="_blank" href="#"
><i class="iconfont icon-qq"
/></a>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import "~/assets/css/sign.css";
import "~/assets/css/iconfont.css";
import registerApi from "@/api/register"
export default {
layout: "sign", //使用页面布局sign.vue
data() {
return {
params: { // 封装注册输入数据
mobile: "",
code: "", //验证码
nickname: "",
password: "",
},
sending: true, //是否发送验证码
second: 60, //倒计时间
codeTest: "获取验证码",
};
},
methods: {
// 通过输入手机号发送验证码
getCodeFun() {
registerApi.sendCode(this.params.mobile)
.then(Response => {
this.sending = false
// 调用倒计时的方法
this.timeDown()
})
},
//倒计时
timeDown() {
let result = setInterval(() => {
--this.second;
this.codeTest = this.second;
if (this.second < 1) {
clearInterval(result);
this.sending = true;
//this.disabled = false;
this.second = 60;
this.codeTest = "获取验证码";
}
}, 1000);
},
// 注册提交的方法
submitRegister() {
registerApi.registerMember(this.params)
.then(Response => {
// 提示注册成功
this.$message({
type: "success",
message: "注册成功",
})
// 跳转登录页面
this.$router.push({ path: "/login" })
})
},
checkPhone(rule, value, callback) {
//debugger
if (!/^1[34578]\d{9}$/.test(value)) {
return callback(new Error("手机号码格式不正确"));
}
return callback();
},
},
};
</script>
3. 修改nginx配置文件 4. 测试 三、用户登录功能前端整合1. 在api文件夹中创建登录的js文件,定义接口login.jsimport request from '@/utils/request'
export default{
//登录的方法
submitLoginUser(userInfo){
return request({
url: `/educenter/member/login`,
method: 'post',
data: userInfo
})
},
// 根据token获取用户信息
getLoginUserInfo(){
return request({
url: `/educenter/member/getMemberInfo`,
method: 'get'
})
},
}2. 在pages文件夹中创建登录页面,调用方法安装js-cookie插件npm install js-cookielogin.vue<template>
<div class="main">
<div class="title">
<a class="active" href="/login">登录</a>
<span>·</span>
<a href="/register">注册</a>
</div>
<div class="sign-up-container">
<el-form ref="userForm" :model="user">
<el-form-item
class="input-prepend restyle"
prop="mobile"
:rules="[
{
required: true,
message: '请输入手机号码',
trigger: 'blur',
},
{ validator: checkPhone, trigger: 'blur' },
]"
>
<div>
<el-input type="text" placeholder="手机号" v-model="user.mobile" />
<i class="iconfont icon-phone" />
</div>
</el-form-item>
<el-form-item
class="input-prepend"
prop="password"
:rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]"
>
<div>
<el-input
type="password"
placeholder="密码"
v-model="user.password"
/>
<i class="iconfont icon-password" />
</div>
</el-form-item>
<div class="btn">
<input
type="button"
class="sign-in-button"
value="登录"
@click="submitLogin()"
/>
</div>
</el-form>
<!-- 更多登录方式 -->
<div class="more-sign">
<h6>社交帐号登录</h6>
<ul>
<li>
<a
id="weixin"
class="weixin"
target="_blank"
href="http://qy.free.idcfengye.com/api/ucenter/weixinLogin/login"
><i class="iconfont icon-weixin"
/></a>
</li>
<li>
<a id="qq" class="qq" target="_blank" href="#"
><i class="iconfont icon-qq"
/></a>
</li>
</ul>
</div>
</div>
</div>
</template>
<script>
import "~/assets/css/sign.css";
import "~/assets/css/iconfont.css";
import cookie from "js-cookie";
import loginApi from "@/api/login";
export default {
layout: "sign",
data() {
return {
user: {
//封装用于登录的用户对象
mobile: "",
password: "",
},
//用于获取接口传来的token中的对象
loginInfo: {},
};
},
methods: {
submitLogin() {
loginApi.submitLoginUser(this.user)
.then(response => {
if (response.data.success) {
//把token存在cookie中、也可以放在localStorage中
//参数1:cookie名称,参数2:具体的值,参数3:作用范围
cookie.set('guli_token', response.data.data.token, { domain: 'localhost' })
//登录成功根据token获取用户信息
loginApi.getLoginUserInfo()
.then(response => {
this.loginInfo = JSON.stringify(response.data.data.userInfo)
//将用户信息记录cookie
cookie.set("guli_ucenter", this.loginInfo, { domain: "localhost" })
//跳转页面
window.location.href = "/";
//this.$router.push({path:'/'})
});
}
});
},
checkPhone(rule, value, callback) {
//debugger
if (!/^1[34578]\d{9}$/.test(value)) {
return callback(new Error("手机号码格式不正确"));
}
return callback();
},
},
};
</script>
<style>
.el-form-item__error {
z-index: 9999999;
}
</style>
3. 在request.js添加拦截器,用于传递token信息import axios from 'axios'
import { MessageBox, Message } from 'element-ui'
import cookie from 'js-cookie'
// 创建axios实例
const service = axios.create({
baseURL: 'http://localhost:9001', // api的base_url
timeout: 20000 // 请求超时时间
})
// http request 拦截器
service.interceptors.request.use(
config => {
//debugger
//判断cookie中是否有名称叫 guli_token的数据
if (cookie.get('guli_token')) {
//把获取到的cookie值放到header中
config.headers['token'] = cookie.get('guli_token');
}
return config
},
err => {
return Promise.reject(err);
})
// http response 拦截器
service.interceptors.response.use(
response => {
//debugger
if (response.data.code == 28004) {
console.log("response.data.resultCode是28004")
// 返回 错误代码-1 清除ticket信息并跳转到登录页面
//debugger
window.location.href = "/login"
return
} else {
if (response.data.code !== 20000) {
//25000:订单支付中,不做任何提示
if (response.data.code != 25000) {
Message({
message: response.data.message || 'error',
type: 'error',
duration: 5 * 1000
})
}
} else {
return response;
}
}
},
error => {
return Promise.reject(error.response) // 返回接口返回的错误信息
})
export default service4. 修改layouts中的default.vue页面显示登录之后的用户信息<script>
import "~/assets/css/reset.css";
import "~/assets/css/theme.css";
import "~/assets/css/global.css";
import "~/assets/css/web.css";
import cookie from "js-cookie";
export default {
data() {
return {
token: "",
loginInfo: {
id: "",
age: "",
avatar: "",
mobile: "",
nickname: "",
sex: "",
}
}
},
created() {
this.showInfo()
},
methods: {
// 退出登录
logout() {
//清空cookie值
cookie.set("guli_token", "", {domain: "localhost"})
cookie.set("guli_ucenter", "", {domain: "localhost"})
//跳转首页面
window.location.href = "/";
},
//创建方法从cookie中获取信息
showInfo() {
//从cookie中获取信息
var userStr = cookie.get("guli_ucenter");
//转字符串转换成json对象(js对象)
if (userStr) {
this.loginInfo = JSON.parse(userStr);
}
}
}
};
</script>
default.vue页面显示登录之后的用户信息<!-- / nav -->
<ul class="h-r-login">
<li v-if="!loginInfo.id" id="no-login">
<a href="/login" title="登录">
<em class="icon18 login-icon">&nbsp;</em>
<span class="vam ml5">登录</span>
</a>
<a href="/register" title="注册">
<span class="vam ml5">注册</span>
</a>
</li>
<li v-if="loginInfo.id" id="is-login-one" class="mr10">
<a id="headerMsgCountId" href="#" title="消息">
<em class="icon18 news-icon">&nbsp;</em>
</a>
<q class="red-point" style="display: none">&nbsp;</q>
</li>
<li v-if="loginInfo.id" id="is-login-two" class="h-r-user">
<a href="/ucenter" title>
<img
:src="loginInfo.avatar"
width="30"
height="30"
class="vam picImg"
alt
/>
<span id="userName" class="vam disIb">{{
loginInfo.nickname
}}</span>
</a>
<a
href="javascript:void(0);"
title="退出"
@click="logout()"
class="ml5"
>退 出</a
>
</li>
<!-- /未登录显示第1 li;登录后显示第2,3 li -->
</ul>
创作不易,如果有帮助到你,请给文章==点个赞和收藏==,让更多的人看到!!!==关注博主==不迷路,内容持续更新中。
HttpClient学习整理
HttpClient简介HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如 Apache Jakarta 上很著名的另外两个开源项目 Cactus 和 HTMLUnit 都使用了 HttpClient。更多信息请关注http://hc.apache.org/HttpClient 功能介绍以下列出的是 HttpClient 提供的主要的功能,要知道更多详细的功能可以参见 HttpClient 的主页。实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)支持自动转向支持 HTTPS 协议支持代理服务器等应用HttpClient来对付各种顽固的WEB服务器转自:http://blog.csdn.net/ambitiontan/archive/2006/01/06/572171.aspx一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中“偷”一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到COOKIE问题的处理。我们知道目前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie,想想当你用java.net.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的“顽固”!再有如通过HTTP来上传文件呢?不需要头疼,这些问题有了“它”就很容易解决了!我们不可能列举所有可能的顽固,我们会针对几种最常见的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache开源组织中的httpclient,它隶属于Jakarta的commons项目,目前的版本是2.0RC2。commons下本来已经有一个net的子项目,但是又把httpclient单独提出来,可见http服务器的访问绝非易事。Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯方式,告诉它你想使用HTTPS方式,剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们,为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所有问题。1. 读取网页(HTTP/HTTPS)内容下面是我们给出的一个简单的例子用来访问某个页面/**
*最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面
*@authorLiudong
*/
public class SimpleClient {
public static void main(String[] args) throws IOException
{
HttpClient client = new HttpClient();
// 设置代理服务器地址和端口
//client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);
// 使用 GET 方法 ,如果服务器需要通过 HTTPS 连接,那只需要将下面 URL 中的 http 换成 https
HttpMethod method=new GetMethod("http://java.sun.com");
//使用POST方法
//HttpMethod method = new PostMethod("http://java.sun.com");
client.executeMethod(method);
//打印服务器返回的状态
System.out.println(method.getStatusLine());
//打印返回的信息
System.out.println(method.getResponseBodyAsString());
//释放连接
method.releaseConnection();
}
}在这个例子中首先创建一个HTTP客户端(HttpClient)的实例,然后选择提交的方法是GET或者POST,最后在HttpClient实例上执行提交的方法,最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用HttpClient的基本流程。其实用一行代码也就可以搞定整个请求的过程,非常的简单!2、使用POST方式提交数据(httpClient3)httpclient使用了单独的一个HttpMethod子类来处理文件的上传,这个类就是MultipartPostMethod,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面这里将给出关于两种模拟上传方式的代码第一种:模拟上传url文件(该方式也适合做普通post请求):/**
* 上传url文件到指定URL
* @param fileUrl 上传图片url
* @param postUrl 上传路径及参数,注意有些中文参数需要使用预先编码 eg : URLEncoder.encode(appName, "UTF-8")
* @return
* @throws IOException
*/
public static String doUploadFile(String postUrl) throws IOException {
if(StringUtils.isEmpty(postUrl))
return null;
String response = "";
PostMethod postMethod = new PostMethod(postUrl);
try {
HttpClient client = new HttpClient();
client.getHttpConnectionManager().getParams()
.setConnectionTimeout(50000);// 设置连接时间
int status = client.executeMethod(postMethod);
if (status == HttpStatus.SC_OK) {
InputStream inputStream = postMethod.getResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(
inputStream));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while ((str = br.readLine()) != null) {
stringBuffer.append(str);
}
response = stringBuffer.toString();
} else {
response = "fail";
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放连接
postMethod.releaseConnection();
}
return response;
}第二种:模拟文件上传到指定位置/**
* 上传文件到指定URL
* @param file
* @param url
* @return
* @throws IOException
*/
public static String doUploadFile(File file, String url) throws IOException {
String response = "";
if (!file.exists()) {
return "file not exists";
}
PostMethod postMethod = new PostMethod(url);
try {
//----------------------------------------------
// FilePart:用来上传文件的类,file即要上传的文件
FilePart fp = new FilePart("file", file);
Part[] parts = { fp };
// 对于MIME类型的请求,httpclient建议全用MulitPartRequestEntity进行包装
MultipartRequestEntity mre = new MultipartRequestEntity(parts,
postMethod.getParams());
postMethod.setRequestEntity(mre);
//---------------------------------------------
HttpClient client = new HttpClient();
client.getHttpConnectionManager().getParams()
.setConnectionTimeout(50000);// 由于要上传的文件可能比较大 , 因此在此设置最大的连接超时时间
int status = client.executeMethod(postMethod);
if (status == HttpStatus.SC_OK) {
InputStream inputStream = postMethod.getResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(
inputStream));
StringBuffer stringBuffer = new StringBuffer();
String str = "";
while ((str = br.readLine()) != null) {
stringBuffer.append(str);
}
response = stringBuffer.toString();
} else {
response = "fail";
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放连接
postMethod.releaseConnection();
}
return response;
}3. 处理页面重定向在JSP/Servlet编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的的区别在于后者是在服务器中实现页面的跳转,也就是说应用容器加载了所要跳转的页面的内容并返回给客户端;而前者是返回一个状态码,这些状态码的可能值见下表,然后客户端读取需要跳转到的页面的URL并重新加载新的页面。就是这样一个过程,所以我们编程的时候就要通过HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可以通过读取HTTP头中的location属性来获取新的地址。下面的代码片段演示如何处理页面的重定向client.executeMethod(post);
System.out.println(post.getStatusLine().toString());
post.releaseConnection();
// 检查是否重定向
int statuscode = post.getStatusCode();
if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) ||
(statuscode ==HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
// 读取新的 URL 地址
Header header=post.getResponseHeader("location");
if (header!=null){
Stringnewuri=header.getValue();
if((newuri==null)||(newuri.equals("")))
newuri="/";
GetMethodredirect=newGetMethod(newuri);
client.executeMethod(redirect);
System.out.println("Redirect:"+redirect.getStatusLine().toString());
redirect.releaseConnection();
}else
System.out.println("Invalid redirect");
}我们可以自行编写两个JSP页面,其中一个页面用response.sendRedirect方法重定向到另外一个页面用来测试上面的例子。
4. 模拟登录开心网本小节应该说是HTTP客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可浏览到想要的页面。因为HTTP协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以JSP/Servlet为例,当浏览器请求一个JSP或者是Servlet的页面时,应用服务器会返回一个参数,名为jsessionid(因不同应用服务器而异),值是一个较长的唯一字符串的Cookie,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息,应用服务器根据读取这个会话标识来获取对应的会话信息。对于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的Cookie中读取当前请求对应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。这样一来,对于HTTP的客户端来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取Cookie值;再次请求登录页面并加入登录页所需的每个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多,可是如果你使用httpclient的话,你甚至连一行代码都无需增加,你只需要先传递登录信息执行登录过程,然后直接访问想要的页面,跟访问一个普通的页面没有任何区别,因为类HttpClient已经帮你做了所有该做的事情了,太棒了!下面的例子实现了模拟登陆开心网并向自己好友发送消息的功能。import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
class Login {
public static String loginurl = "https://security.kaixin001.com/login/login_post.php";
static Cookie[] cookies = {};
static HttpClient httpClient = new HttpClient();
static String email = "xxx@qq.com";//你的email
static String psw = "xxx";//你的密码
// 消息发送的action
String url = "http://www.kaixin001.com/home/";
public static void getUrlContent()
throws Exception {
HttpClientParams httparams = new HttpClientParams();
httparams.setSoTimeout(30000);
httpClient.setParams(httparams);
httpClient.getHostConfiguration().setHost("www.kaixin001.com", 80);
httpClient.getParams().setParameter(
HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
PostMethod login = new PostMethod(loginurl);
login.addRequestHeader("Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8");
NameValuePair Email = new NameValuePair("loginemail", email);// 邮箱
NameValuePair password = new NameValuePair("password", psw);// 密码
// NameValuePair code = new NameValuePair( "code"
// ,"????");//有时候需要验证码,暂时未解决
NameValuePair[] data = { Email, password };
login.setRequestBody(data);
httpClient.executeMethod(login);
int statuscode = login.getStatusCode();
System.out.println(statuscode + "-----------");
String result = login.getResponseBodyAsString();
System.out.println(result+"++++++++++++");
cookies = httpClient.getState().getCookies();
System.out.println("==========Cookies============");
int i = 0;
for (Cookie c : cookies) {
System.out.println(++i + ": " + c);
}
httpClient.getState().addCookies(cookies);
// 当state为301或者302说明登陆页面跳转了,登陆成功了
if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY)
|| (statuscode == HttpStatus.SC_MOVED_PERMANENTLY)
|| (statuscode == HttpStatus.SC_SEE_OTHER)
|| (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
// 读取新的 URL 地址
Header header = login.getResponseHeader("location");
// 释放连接
login.releaseConnection();
System.out.println("获取到跳转header>>>" + header);
if (header != null) {
String newuri = header.getValue();
if ((newuri == null) || (newuri.equals("")))
newuri = "/";
GetMethod redirect = new GetMethod(newuri);
//
redirect.setRequestHeader("Cookie", cookies.toString());
httpClient.executeMethod(redirect);
System.out.println("Redirect:"
+ redirect.getStatusLine().toString());
redirect.releaseConnection();
} else
System.out.println("Invalid redirect");
} else {
// 用户名和密码没有被提交,当登陆多次后需要验证码的时候会出现这种未提交情况
System.out.println("用户没登陆");
System.exit(1);
}
}
public static void sendMsg() throws Exception {
// 登录后发消息
System.out.println("*************发消息***********");
String posturl = "http://www.kaixin001.com/msg/post.php";
PostMethod poster = new PostMethod(posturl);
poster.addRequestHeader("Content-Type",
"application/x-www-form-urlencoded; charset=UTF-8");
poster.setRequestHeader("Cookie", cookies.toString());
NameValuePair uids = new NameValuePair("uids", "89600585");// 发送的好友对象的id,此处换成你的好友id
NameValuePair content = new NameValuePair("content", "你好啊!");// 需要发送的信息的内容
NameValuePair liteeditor_0 = new NameValuePair("liteeditor_0", "你好啊!");// 需要发送的信息的内容
NameValuePair texttype = new NameValuePair("texttype", "plain");
NameValuePair send_separate = new NameValuePair("send_separate", "0");
NameValuePair service = new NameValuePair("service", "0");
NameValuePair[] msg = { uids, content, texttype, send_separate, service,liteeditor_0 };
poster.setRequestBody(msg);
httpClient.executeMethod(poster);
String result = poster.getResponseBodyAsString();
System.out.println(result+"++++++++++++");
//System.out.println(StreamOut(result, "iso8859-1"));
int statuscode = poster.getStatusCode();
System.out.println(statuscode + "-----------");
if(statuscode == 301 || statuscode == 302){
// 读取新的 URL 地址
Header header = poster.getResponseHeader("location");
System.out.println("获取到跳转header>>>" + header);
if (header != null) {
String newuri = header.getValue();
if ((newuri == null) || (newuri.equals("")))
newuri = "/";
GetMethod redirect = new GetMethod(newuri);
//
redirect.setRequestHeader("Cookie", cookies.toString());
httpClient.executeMethod(redirect);
System.out.println("Redirect:"
+ redirect.getStatusLine().toString());
redirect.releaseConnection();
} else
System.out.println("Invalid redirect");
}
poster.releaseConnection();
}
public static String StreamOut(InputStream txtis, String code)
throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(txtis,
code));
String tempbf;
StringBuffer html = new StringBuffer(100);
while ((tempbf = br.readLine()) != null) {
html.append(tempbf + "\n");
}
return html.toString();
}
}5. 提交XML格式参数提交XML格式的参数很简单,仅仅是一个提交时候的ContentType问题,下面的例子演示从文件文件中读取XML信息并提交给服务器的过程,该过程可以用来测试Web服务。import java.io.File;
import java.io.FileInputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
import org.apache.commons.httpclient.methods.PostMethod;
/**
*用来演示提交XML格式数据的例子
*/
public class PostXMLClient {
public static void main(String[] args) throws Exception {
File input = new File(“test.xml”);
PostMethod post = new PostMethod(“http://localhost:8080/httpclient/xml.jsp”);
// 设置请求的内容直接从文件中读取
post.setRequestBody( new FileInputStream(input));
if (input.length() < Integer.MAX_VALUE)
post.setRequestContentLength(input.length());
else
post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);
// 指定请求内容的类型
post.setRequestHeader( "Content-type" , "text/xml; charset=GBK" );
HttpClient httpclient = new HttpClient();
int result = httpclient.executeMethod(post);
System.out.println( "Response status code: " + result);
System.out.println( "Response body: " );
System.out.println(post.getResponseBodyAsString());
post.releaseConnection();
}
}6. 访问启用认证的页面我们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略,httpclient支持三种认证方式包括:基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全;摘要认证是在HTTP 1.1中加入的认证方式,而NTLM则是微软公司定义的而不是通用的规范,最新版本的NTLM是比摘要认证还要安全的一种方式。下面例子是从httpclient的CVS服务器中下载的,它简单演示如何访问一个认证保护的页面:import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.GetMethod;
public class BasicAuthenticationExample {
public BasicAuthenticationExample() {
}
public static void main(String[] args) throws Exception {
HttpClient client = new HttpClient();
client.getState().setCredentials( "www.verisign.com" , "realm" , new UsernamePasswordCredentials( "username" , "password" ) );
GetMethod get = new GetMethod( "https://www.verisign.com/products/index.html" );
get.setDoAuthentication( true );
int status = client.executeMethod( get );
System.out.println(status+ "\n" + get.getResponseBodyAsString());
get.releaseConnection();
}
}7. 多线程模式下使用多线程同时访问httpclient,例如同时从一个站点上下载多个文件。对于同一个HttpConnection同一个时间只能有一个线程访问,为了保证多线程工作环境下不产生冲突,httpclient使用了一个多线程连接管理器的类:MultiThreadedHttpConnectionManager,要使用这个类很简单,只需要在构造HttpClient实例的时候传入即可,代码如下:MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();HttpClient client = new HttpClient(connectionManager);以后尽管访问client实例即可。httpClient完整封装HttpInvoke.java:封装了HttpClient调度的必要参数设置,以及post,get等常用方法import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import java.util.Iterator;
import java.util.Map;
import java.net.SocketTimeoutException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class HttpInvoker {
private Log logger = LogFactory.getLog(HttpInvoker.class);
private static HttpInvoker httpInvoker = new HttpInvoker();
private HttpClient client = null;
private String charset = "gbk";
private int timeout = 10000;
private boolean useProxy = false;
private String proxyHost = null;
private int proxyPort;
private String proxyUsername = null;
private String proxyPassword = null;
private boolean initialized = false;
public static HttpInvoker getInstance() {
return httpInvoker;
}
private HttpInvoker() {
client = new HttpClient(new MultiThreadedHttpConnectionManager());
client.getParams().setParameter("http.protocol.content-charset", "gbk");
client.getParams().setContentCharset("gbk");
client.getParams().setSoTimeout(timeout);
}
public HttpInvoker(String charset, int timeout, boolean useProxy,
String proxyHost, int proxyPort, String proxyUsername,
String proxyPassword) {
client = new HttpClient(new MultiThreadedHttpConnectionManager());
if(charset != null && !charset.trim().equals("")) {
this.charset = charset;
}
if(timeout > 0) {
this.timeout = timeout;
}
client.getParams().setParameter("http.protocol.content-charset", charset);
client.getParams().setContentCharset(charset);
client.getParams().setSoTimeout(timeout);
if(useProxy && proxyHost != null &&
!proxyHost.trim().equals("") && proxyPort > 0) {
HostConfiguration hc = new HostConfiguration();
hc.setProxy(proxyHost, proxyPort);
client.setHostConfiguration(hc);
if (proxyUsername != null && !proxyUsername.trim().equals("") &&
proxyPassword != null && !proxyPassword.trim().equals("")) {
client.getState().setProxyCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(proxyUsername, proxyPassword));
}
}
initialized = true;
logger.debug("HttpInvoker初始化完成");
}
public synchronized void init() {
if(charset != null && !charset.trim().equals("")) {
client.getParams().setParameter("http.protocol.content-charset", charset);
client.getParams().setContentCharset(charset);
}
if(timeout > 0) {
client.getParams().setSoTimeout(timeout);
}
if(useProxy && proxyHost != null &&
!proxyHost.trim().equals("") && proxyPort > 0) {
HostConfiguration hc = new HostConfiguration();
hc.setProxy(proxyHost, proxyPort);
client.setHostConfiguration(hc);
if (proxyUsername != null && !proxyUsername.trim().equals("") &&
proxyPassword != null && !proxyPassword.trim().equals("")) {
client.getState().setProxyCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(proxyUsername, proxyPassword));
}
}
initialized = true;
logger.debug("HttpInvoker初始化完成");
}
public String invoke(String url) throws Exception {
return invoke(url, null, false);
}
public String invoke(String url, Map params, boolean isPost) throws Exception {
logger.debug("HTTP调用[" + (isPost?"POST":"GET") + "][" + url + "][" + params + "]");
HttpMethod httpMethod = null;
String result = "";
try {
if(isPost && params != null && params.size() > 0) {
Iterator paramKeys = params.keySet().iterator();
httpMethod = new PostMethod(url);
NameValuePair[] form = new NameValuePair[params.size()];
int formIndex = 0;
while(paramKeys.hasNext()) {
String key = (String)paramKeys.next();
Object value = params.get(key);
if(value != null && value instanceof String && !value.equals("")) {
form[formIndex] = new NameValuePair(key, (String)value);
formIndex++;
} else if(value != null && value instanceof String[] &&
((String[])value).length > 0) {
NameValuePair[] tempForm =
new NameValuePair[form.length + ((String[])value).length - 1];
for(int i=0; i<formIndex; i++) {
tempForm[i] = form[i];
}
form = tempForm;
for(String v : (String[])value) {
form[formIndex] = new NameValuePair(key, (String)v);
formIndex++;
}
}
}
((PostMethod)httpMethod).setRequestBody(form);
} else {
if(params != null && params.size() > 0) {
Iterator paramKeys = params.keySet().iterator();
StringBuffer getUrl = new StringBuffer(url.trim());
if(url.trim().indexOf("?") > -1) {
if(url.trim().indexOf("?") < url.trim().length()-1 &&
url.trim().indexOf("&") < url.trim().length()-1) {
getUrl.append("&");
}
} else {
getUrl.append("?");
}
while(paramKeys.hasNext()) {
String key = (String)paramKeys.next();
Object value = params.get(key);
if(value != null && value instanceof String && !value.equals("")) {
getUrl.append(key).append("=").append(value).append("&");
} else if(value != null && value instanceof String[] &&
((String[])value).length > 0) {
for(String v : (String[])value) {
getUrl.append(key).append("=").append(v).append("&");
}
}
}
if(getUrl.lastIndexOf("&") == getUrl.length()-1) {
httpMethod = new GetMethod(getUrl.substring(0, getUrl.length()-1));
} else {
httpMethod = new GetMethod(getUrl.toString());
}
} else {
httpMethod = new GetMethod(url);
}
}
client.executeMethod(httpMethod);
// result = httpMethod.getResponseBodyAsString();
BufferedReader reader = new BufferedReader(new InputStreamReader(
httpMethod.getResponseBodyAsStream(),"ISO-8859-1"));
String line = null;
String html = null;
while((line = reader.readLine()) != null){
if(html == null) {
html = "";
} else {
html += "\r\n";
}
html += line;
}
if(html != null) {
result = new String(html.getBytes("ISO-8859-1"), charset);
}
} catch (SocketTimeoutException e) {
logger.error("连接超时[" + url + "]");
throw e;
} catch (java.net.ConnectException e) {
logger.error("连接失败[" + url + "]");
throw e;
} catch (Exception e) {
logger.error("连接时出现异常[" + url + "]");
throw e;
} finally {
if (httpMethod != null) {
try {
httpMethod.releaseConnection();
} catch (Exception e) {
logger.error("释放网络连接失败[" + url + "]");
throw e;
}
}
}
return result;
}
public void setCharset(String charset) {
this.charset = charset;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public void setProxyHost(String proxyHost) {
this.proxyHost = proxyHost;
}
public void setProxyPort(int proxyPort) {
this.proxyPort = proxyPort;
}
public void setProxyUsername(String proxyUsername) {
this.proxyUsername = proxyUsername;
}
public void setProxyPassword(String proxyPassword) {
this.proxyPassword = proxyPassword;
}
public void setUseProxy(boolean useProxy) {
this.useProxy = useProxy;
}
public synchronized boolean isInitialized() {
return initialized;
}
}http访问网络的代理ip和端口,还有使用用户及密码都可以在Spring容器中注入进来:<bean id="httpInvoker" class="HttpInvoker">
<constructor-arg type="java.lang.String" value="gbk" /><!--useProxy-->
<constructor-arg type="int" value="10000" /><!--useProxy-->
<constructor-arg type="boolean" value="true" /><!--useProxy-->
<!--代理地址 -->
<constructor-arg type="java.lang.String" value="192.168.1.1" />
<constructor-arg type="int" value="8080" />
<constructor-arg type="java.lang.String" value="" /><!--用户名-->
<constructor-arg type="java.lang.String" value="" /><!--密码-->
</bean>使用方式:postMap<String,String> params = new HashMap<String,String>();
params.put("check", check);
String result = httpInvoker.invoke( "someURL", params, true);使用方式:getString content = httpInvoker.invoke(url);参考资料:httpclient首页: http://jakarta.apache.org/commons/httpclient/关于NTLM是如何工作: http://davenport.sourceforge.net/ntlm.html--------------------------------------------HttpClient入门http://blog.csdn.net/ambitiontan/archive/2006/01/07/572644.aspxJakarta Commons HttpClient 学习笔记http://blog.csdn.net/cxl34/archive/2005/01/19/259051.aspxCookies,SSL,httpclient的多线程处理,HTTP方法http://blog.csdn.net/bjbs_270/archive/2004/11/05/168233.aspxHttpClient 学习整理https://download.csdn.net/download/qq_41570658/16036862