<script>alert('sss')</script>
<script>window.location.href='http://www.hao.com';</script>
// 重写HttpServletRequestWrapper 防止XSS攻击
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private HttpServletRequest request;
/**
* @param request
*/
public XssHttpServletRequestWrapper(HttpServletRequest
request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
// 过滤getParameter参数 检查是否有特殊字符
String value = super.getParameter(name);
System.out.println("value:" + value);
if (!StringUtils.isEmpty(value)) {
// 将中文转换为字符编码格式,将特殊字符变为html源代码保存
value = StringEscapeUtils.escapeHtml(value);
System.out.println("newValue:" + value);
}
return value;
}
}
// SpringBoot启动加上@ServletComponentScan
@SpringBootApplication
@ServletComponentScan
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
#{}
传参数方式,不要使用${}
传参数,因为${}
传参数方式,可能会受到sql语句攻击。http://127.0.0.1:8080/login?userName='liusi'&password='123'
http://127.0.0.1:8080/login?userName='liusi'&password='123' or 1=1
@RestController
public class LoginController {
@Autowired
private UserMapper userMapper;
@RequestMapping("/login")
public String login(UserEntity userEntity) {
System.out.println("账号密码信息:userEntity:" + userEntity.toString());
UserEntity login = userMapper.login(userEntity);
return login == null ? "登陆失败!" : "登陆成功!";
}
}
public interface UserMapper {
@Select(" SELECT * FROM user_info where userName=${userName} and password=${password}")
public UserEntity login(UserEntity userEntity);
}
#{}
解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个#{}
被解析为一个参数占位符,可以防止SQL注入问题。${}
: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。@WebFilter(filterName = "imgFilter", urlPatterns = "/imgs/*")
public class ImgFilter implements Filter {
@Value("${domain.name}")
private String domainName;
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String referer = req.getHeader("Referer");
if (StringUtils.isEmpty(referer)) {
request.getRequestDispatcher("/imgs/error.png").forward(request, response);
return;
}
String domain = getDomain(referer);
if (!domain.equals(domainName)) {
request.getRequestDispatcher("/imgs/error.png").forward(request, response);
return;
}
chain.doFilter(request, response);
}
/**
* 获取url对应的域名
*
* @param url
* @return
*/
public String getDomain(String url) {
String result = "";
int j = 0, startIndex = 0, endIndex = 0;
for (int i = 0; i < url.length(); i++) {
if (url.charAt(i) == '/') {
j++;
if (j == 2)
startIndex = i;
else if (j == 3)
endIndex = i;
}
}
result = url.substring(startIndex + 1, endIndex);
return result;
}
public void destroy() {
}
}
黑客使用抓包工具分析Http请求,在忘记密码找回时,需要发送一套短信验证码,如果验证码数字比较短的话,很容易使用暴力破解方式攻击破。
例如:使用短信验证码,在忘记密码短信找回中有一个短信Code(短信验证码)
您现在要找回您的密码,您的找回短信验证码为6440
破解思路(暴力破解) :假设验证码为4位数字。
使用Java程序HttpClient技术开启多线程技术调用找回密码接口暴力破解生成对应的在4位数字以内验证码进行验证,如果一旦验证成功,就破解成功了。
防御手段:
1.忘记密码验证码最好在6-8位。
2.找回验证码最好不要是单纯全部都是数字
3.建议验证码数字+其他字符混合(字母)
4.一旦频繁调用接口验证时,应该使用图形验证码拦截,防止机器模拟,如果找回密码接口重试五次以上还是错误(出现图形验证码)
5.配置防DOS,限流,限制IP访问,使用黑名单和白名单机制,防御攻击
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form action="/load/UploadServlet" method="post"
enctype="multipart/form-data">
<input type="file" name="file" /> <input type="submit" value="submit" />
</form>
</body>
</html>
public class UploadServletextends HttpServlet {
/**
* 文件上传
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String root = request.getServletContext().getRealPath("/upload");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List<FileItem> list = upload.parseRequest(request);
for (FileItem it : list) {
// 如果是file文件类型
if (!it.isFormField()) {
it.write(new File(root + "/" + it.getName()));
response.getWriter().write("success");
}
}
} catch (Exception e) {
try {
response.getWriter().write("exception");
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
}
<%@page import="java.io.File"%>
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<%
String root ="Hao/Desktop/Img";
File file = new File(root);
file.delete();
%>
// 判断文件流是否为图片格式
/**
* 文件上传
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
String root = request.getServletContext().getRealPath("/upload");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
try {
List<FileItem> list = upload.parseRequest(request);
for (FileItem it : list) {
// 如果是file文件类型
if (!it.isFormField()) {
FileType fileType = getFileType(it.getInputStream());
if (fileType == null) {
// 非图片格式
response.getWriter().write("fail");
return;
}
String imgValue = fileType.getValue();
System.out.println("imgValue:" + imgValue);
// 是图片格式
it.write(new File(root + "/" + it.getName()));
response.getWriter().write("success");
}
}
} catch (Exception e) {
try {
response.getWriter().write("exception");
} catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
}
// 判断文件是图片格式
public static FileType getFileType(InputStream is) throws IOException {
byte[] src = new byte[28];
is.read(src, 0, 28);
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v).toUpperCase();
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
FileType[] fileTypes = FileType.values();
for (FileType fileType : fileTypes) {
if (stringBuilder.toString().startsWith(fileType.getValue())) {
return fileType;
}
}
return null;
}
步骤:
1.生成令牌接口
2.接口中获取令牌验证
生成令牌接口
public class TokenUtils {
private static Map<String, Object> tokenMap = new ConcurrentHashMap<String, Object>();
// 获取token
public static synchronized String getToken() {
// 1.生成令牌
String token = "token-" + System.currentTimeMillis();
// 2.存入tokenMap
tokenMap.put(token, token);
return token;
}
// 验证token,并且删除对应的token
public static Boolean exisToken(String token) {
// 1.从集合中获取token
Object result = tokenMap.get(token);
if (result == null) {
return false;
}
// 2.删除对应的token
tokenMap.remove(token);
return true;
}
}
@RestController
public class OrderController {
@Autowired
private OrderMapper orderMapper;
// 获取Token
@RequestMapping("/getToken")
public String getToken() {
return TokenUtils.getToken();
}
// 验证Token
@RequestMapping(value = "/addOrder", produces = "application/json; charset=utf-8")
public String addOrder(@RequestBody OrderEntity orderEntity, HttpServletRequest request) {
String token = request.getHeader("token");
if (StringUtils.isEmpty(token)) {
return "参数错误!";
}
if (!TokenUtils.exisToken(token)) {
return "请勿重复提交!";
}
int result = orderMapper.addOrder(orderEntity);
return result > 0 ? "添加成功" : "添加失败" + "";
}
}
@Component
public class BaseRedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
public void setString(String key, Object data, Long timeout) {
if (data instanceof String) {
String value = (String) data;
stringRedisTemplate.opsForValue().set(key, value);
}
if (timeout != null) {
stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
}
}
public Object getString(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
public void delKey(String key) {
stringRedisTemplate.delete(key);
}
}
@Component
public class RedisTokenUtils {
private long timeout = 60 * 60;
@Autowired
private BaseRedisService baseRedisService;
// 将token存入在redis
public String getToken() {
String token = "token" + System.currentTimeMillis();
baseRedisService.setString(token, token, timeout);
return token;
}
public boolean findToken(String tokenKey) {
String token = (String) baseRedisService.getString(tokenKey);
if (StringUtils.isEmpty(token)) {
return false;
}
// token 获取成功后 删除对应tokenMapstoken
baseRedisService.delKey(token);
return true;
}
}
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtApiIdempotent {
String value();
}
@Aspect
@Component
public class ExtApiAopIdempotent {
@Autowired
private RedisTokenUtils redisTokenUtils;
@Pointcut("execution(public * com.hao.controller.*.*(..))")
public void rlAop() {
}
@Around("rlAop()")
public Object doBefore(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
ExtApiIdempotent extApiIdempotent = signature.getMethod().getDeclaredAnnotation(ExtApiIdempotent.class);
if (extApiIdempotent == null) {
// 直接执行程序
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
// 代码步骤:
// 1.获取令牌 存放在请求头中
HttpServletRequest request = getRequest();
String token = request.getHeader("token");
if (StringUtils.isEmpty(token)) {
response("参数错误!");
return null;
}
// 2.判断令牌是否在缓存中有对应的令牌
// 3.如何缓存没有该令牌的话,直接报错(请勿重复提交)
// 4.如何缓存有该令牌的话,直接执行该业务逻辑
// 5.执行完业务逻辑之后,直接删除该令牌。
if (!redisTokenUtils.findToken(token)) {
response("请勿重复提交!");
return null;
}
Object proceed = proceedingJoinPoint.proceed();
return proceed;
}
public HttpServletRequest getRequest() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
return request;
}
public void response(String msg) throws IOException {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletResponse response = attributes.getResponse();
response.setHeader("Content-type", "text/html;charset=UTF-8");
PrintWriter writer = response.getWriter();
try {
writer.println(msg);
} catch (Exception e) {
} finally {
writer.close();
}
}
}
// 从redis中获取Token
@RequestMapping("/redisToken")
public String RedisToken() {
return redisTokenUtils.getToken();
}
// 验证Token
@RequestMapping(value = "/addOrderExtApiIdempotent", produces = "application/json; charset=utf-8")
@ExtApiIdempotent
public String addOrderExtApiIdempotent(@RequestBody OrderEntity orderEntity, HttpServletRequest request) {
int result = orderMapper.addOrder(orderEntity);
return result > 0 ? "添加成功" : "添加失败" + "";
}
@Target(value = ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtApiToken {
}
// 前置通知转发Token参数
@Before("rlAop()")
public void before(JoinPoint point) {
MethodSignature signature = (MethodSignature) point.getSignature();
ExtApiToken extApiToken = signature.getMethod().getDeclaredAnnotation(ExtApiToken.class);
if (extApiToken != null) {
extApiToken();
}
}
@RestController
public class OrderController {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RedisTokenUtils redisTokenUtils;
// 从redis中获取Token
@RequestMapping("/redisToken")
public String RedisToken() {
return redisTokenUtils.getToken();
}
// 验证Token
@RequestMapping(value = "/addOrderExtApiIdempotent", produces = "application/json; charset=utf-8")
@ExtApiIdempotent(value = ConstantUtils.EXTAPIHEAD)
public String addOrderExtApiIdempotent(@RequestBody OrderEntity orderEntity, HttpServletRequest request) {
int result = orderMapper.addOrder(orderEntity);
return result > 0 ? "添加成功" : "添加失败" + "";
}
}
@Controller
public class OrderPageController {
@Autowired
private OrderMapper orderMapper;
@RequestMapping("/indexPage")
@ExtApiToken
public String indexPage(HttpServletRequest req) {
return "indexPage";
}
@RequestMapping("/addOrderPage")
@ExtApiIdempotent(value = ConstantUtils.EXTAPIFROM)
public String addOrder(OrderEntity orderEntity) {
int addOrder = orderMapper.addOrder(orderEntity);
return addOrder > 0 ? "success" : "fail";
}
}
原理:为每个合作机构创建对应的appid、app_secret,生成对应的access_token(有效期2小时),在调用外网开放接口的时候,必须传递有效的access_token。
数据库表设计
CREATE TABLE `m_app` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(255) DEFAULT NULL,
`app_id` varchar(255) DEFAULT NULL,
`app_secret` varchar(255) DEFAULT NULL,
`is_flag` varchar(255) DEFAULT NULL,
`access_token` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
App_Name 表示机构名称
App_ID 应用id
App_Secret 应用密钥 (可更改)
Is_flag 是否可用 (是否对某个机构开放)
access_token 上一次access_token
// 创建获取getAccessToken
@RestController
@RequestMapping(value = "/auth")
public class AuthController extends BaseApiService {
@Autowired
private BaseRedisService baseRedisService;
private long timeToken = 60 * 60 * 2;
@Autowired
private AppMapper appMapper;
// 使用appId+appSecret 生成AccessToke
@RequestMapping("/getAccessToken")
public ResponseBase getAccessToken(AppEntity appEntity) {
AppEntity appResult = appMapper.findApp(appEntity);
if (appResult == null) {
return setResultError("没有对应机构的认证信息");
}
int isFlag = appResult.getIsFlag();
if (isFlag == 1) {
return setResultError("您现在没有权限生成对应的AccessToken");
}
// ### 获取新的accessToken 之前删除之前老的accessToken
// 从redis中删除之前的accessToken
String accessToken = appResult.getAccessToken();
baseRedisService.delKey(accessToken);
// 生成的新的accessToken
String newAccessToken = newAccessToken(appResult.getAppId());
JSONObject jsonObject = new JSONObject();
jsonObject.put("accessToken", newAccessToken);
return setResultSuccessData(jsonObject);
}
private String newAccessToken(String appId) {
// 使用appid+appsecret 生成对应的AccessToken 保存两个小时
String accessToken = TokenUtils.getAccessToken();
// 保证在同一个事物redis 事物中
// 生成最新的token key为accessToken value 为 appid
baseRedisService.setString(accessToken, appId, timeToken);
// 表中保存当前accessToken
appMapper.updateAccessToken(accessToken, appId);
return accessToken;
}
}
//验证AccessToken 是否正确
@Component
public class AccessTokenInterceptor extends BaseApiService implements HandlerInterceptor {
@Autowired
private BaseRedisService baseRedisService;
/**
* 进入controller层之前拦截请求
*
* @param httpServletRequest
* @param httpServletResponse
* @param o
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o)
throws Exception {
System.out.println("---------------------开始进入请求地址拦截----------------------------");
String accessToken = httpServletRequest.getParameter("accessToken");
// 判断accessToken是否空
if (StringUtils.isEmpty(accessToken)) {
// 参数Token accessToken
resultError(" this is parameter accessToken null ", httpServletResponse);
return false;
}
String appId = (String) baseRedisService.getString(accessToken);
if (StringUtils.isEmpty(appId)) {
// accessToken 已经失效!
resultError(" this is accessToken Invalid ", httpServletResponse);
return false;
}
// 正常执行业务逻辑...
return true;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
ModelAndView modelAndView) throws Exception {
System.out.println("--------------处理请求完成后视图渲染之前的处理操作---------------");
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
System.out.println("---------------视图渲染之后的操作-------------------------0");
}
// 返回错误提示
public void resultError(String errorMsg, HttpServletResponse httpServletResponse) throws IOException {
PrintWriter printWriter = httpServletResponse.getWriter();
printWriter.write(new JSONObject().toJSONString(setResultError(errorMsg)));
}
}
QQ登录OAuth2.0采用OAuth2.0标准协议来进行用户身份验证和获取用户授权,相对于之前的OAuth1.0协议,其认证流程更简单和安全。
OAuth2.0总体处理流程
1 第一步:用户同意授权,获取code
2 第二步:通过code换取网页授权access_token
3 第三步:刷新access_token(如果需要)
4 第四步:拉取用户信息(需scope为 snsapi_userinfo)
/**
* MD5加盐加密
*/
public class PasswordUtil {
/**
* 生成含有随机盐的密码
*/
public static String generate(String password) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
password = md5Hex(password + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = password.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = password.charAt(i / 3 * 2 + 1);
}
return new String(cs);
}
/**
* 校验密码是否正确
*/
public static boolean verify(String password, String md5) {
char[] cs1 = new char[32];
char[] cs2 = new char[16];
for (int i = 0; i < 48; i += 3) {
cs1[i / 3 * 2] = md5.charAt(i);
cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
cs2[i / 3] = md5.charAt(i + 1);
}
String salt = new String(cs2);
return md5Hex(password + salt).equals(new String(cs1));
}
/**
* 获取十六进制字符串形式的MD5摘要
*/
public static String md5Hex(String src) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bs = md5.digest(src.getBytes());
return new String(new Hex().encode(bs));
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
// 加密+加盐
String password1 = generate("admin");
System.out.println("结果:" + password1 + " 长度:" + password1.length());
// 解码
System.out.println(verify("admin", password1));
// 加密+加盐
String password2 = generate("admin");
System.out.println("结果:" + password2 + " 长度:" + password2.length());
// 解码
System.out.println(verify("admin", password2));
}
}
AES(高级加密标准):DES升级版,算法出自Rinjindael
对称密码的优点
用户只需记忆一个密钥,就可用于加密、解密;
与非对称加密方法相比,加密解密的计算量小,速度快,简单易用,适合于对海量数据进行加密处理 。
对称密码的缺点
如果密钥交换不安全,密钥的安全性就会丧失。特别是在电子商务环境下,当客户是未知的、不可信的实体时,如何使客户安全地获得密钥就成为一大难题。
如果密钥多个用户被共享,不能提供抗抵赖性
对称密码案例
假设Alice和Bob是认识的,两人为了保证通信消息不被其它人截取,预先约定了一个密码,用来加密在他们之间传送的消息,这样即使有人截取了消息没有密码也无法知道消息的内容。由此便实现了机密性。
DES加密工具类
/**
* DES加密介绍 DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
* 后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
* 24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现 。
* 注意:DES加密和解密过程中,密钥长度都必须是8的倍数
*/
public class DES {
public DES() {
}
// 测试
public static void main(String args[]) {
// 待加密内容
String str = "cryptology";
// 密码,长度要是8的倍数
String password = "95880288";
byte[] result = DES.encrypt(str.getBytes(), password);
System.out.println("加密后:" + new String(result));
// 直接将如上内容解密
try {
byte[] decryResult = DES.decrypt(result, password);
System.out.println("解密后:" + new String(decryResult));
} catch (Exception e1) {
e1.printStackTrace();
}
}
/**
* 加密
*
* @param datasource
* byte[]
* @param password
* String
* @return byte[]
*/
public static byte[] encrypt(byte[] datasource, String password) {
try {
SecureRandom random = new SecureRandom();
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂,然后用它把DESKeySpec转换成
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成加密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
// 现在,获取数据并加密
// 正式执行加密操作
return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
/**
* 解密
*
* @param src
* byte[]
* @param password
* String
* @return byte[]
* @throws Exception
*/
public static byte[] decrypt(byte[] src, String password) throws Exception {
// DES算法要求有一个可信任的随机数源
SecureRandom random = new SecureRandom();
// 创建一个DESKeySpec对象
DESKeySpec desKey = new DESKeySpec(password.getBytes());
// 创建一个密匙工厂
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
// Cipher
// 对象
// 将DESKeySpec对象转换成SecretKey对象
SecretKey securekey = keyFactory.generateSecret(desKey);
// Cipher对象实际完成解密操作
Cipher cipher = Cipher.getInstance("DES");
// 用密匙初始化Cipher对象
cipher.init(Cipher.DECRYPT_MODE, securekey, random);
// 真正开始解密操作
return cipher.doFinal(src);
}
}
机密性、完整性、抗抵赖性
使用过程:
乙方生成两把密钥(公钥和私钥)
甲方获取乙方的公钥,然后用它对信息加密。
乙方得到加密后的信息,用私钥解密,乙方也可用私钥加密字符串
甲方获取乙方私钥加密数据,用公钥解密
优点:难破解
缺点: 加密速度慢
常用算法:
RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)
/**
* RSA加解密工具类
*
* @author QiuFeihu
*
*/
public class RSAUtil {
public static String publicKey; // 公钥
public static String privateKey; // 私钥
/**
* 生成公钥和私钥
*/
public static void generateKey() {
// 1.初始化秘钥
KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom sr = new SecureRandom(); // 随机数生成器
keyPairGenerator.initialize(512, sr); // 设置512位长的秘钥
KeyPair keyPair = keyPairGenerator.generateKeyPair(); // 开始创建
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
// 进行转码
publicKey = Base64.encodeBase64String(rsaPublicKey.getEncoded());
// 进行转码
privateKey = Base64.encodeBase64String(rsaPrivateKey.getEncoded());
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 私钥匙加密或解密
*
* @param content
* @param privateKeyStr
* @return
*/
public static String encryptByprivateKey(String content, String privateKeyStr, int opmode) {
// 私钥要用PKCS8进行处理
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyStr));
KeyFactory keyFactory;
PrivateKey privateKey;
Cipher cipher;
byte[] result;
String text = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
// 还原Key对象
privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(opmode, privateKey);
if (opmode == Cipher.ENCRYPT_MODE) { // 加密
result = cipher.doFinal(content.getBytes());
text = Base64.encodeBase64String(result);
} else if (opmode == Cipher.DECRYPT_MODE) { // 解密
result = cipher.doFinal(Base64.decodeBase64(content));
text = new String(result, "UTF-8");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return text;
}
/**
* 公钥匙加密或解密
*
* @param content
* @param privateKeyStr
* @return
*/
public static String encryptByPublicKey(String content, String publicKeyStr, int opmode) {
// 公钥要用X509进行处理
X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyStr));
KeyFactory keyFactory;
PublicKey publicKey;
Cipher cipher;
byte[] result;
String text = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
// 还原Key对象
publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
cipher = Cipher.getInstance("RSA");
cipher.init(opmode, publicKey);
if (opmode == Cipher.ENCRYPT_MODE) { // 加密
result = cipher.doFinal(content.getBytes());
text = Base64.encodeBase64String(result);
} else if (opmode == Cipher.DECRYPT_MODE) { // 解密
result = cipher.doFinal(Base64.decodeBase64(content));
text = new String(result, "UTF-8");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return text;
}
// 测试方法
public static void main(String[] args) {
/**
* 注意: 私钥加密必须公钥解密 公钥加密必须私钥解密
*/
System.out.println("-------------生成两对秘钥,分别发送方和接收方保管-------------");
RSAUtil.generateKey();
System.out.println("公钥匙给接收方:" + RSAUtil.publicKey);
System.out.println("私钥给发送方:" + RSAUtil.privateKey);
System.out.println("-------------第一个例子子,私钥加密公钥解密-------------");
// String textsr = "早啊,你吃早饭了吗?O(∩_∩)O~";
// // 私钥加密
// String cipherText = RSAUtil.encryptByprivateKey(textsr,
// RSAUtil.privateKey, Cipher.ENCRYPT_MODE);
// System.out.println("发送方用私钥加密后:" + cipherText);
// // 公钥解密
// String text = RSAUtil.encryptByPublicKey(cipherText,
// RSAUtil.publicKey, Cipher.DECRYPT_MODE);
// System.out.println("接收方用公钥解密后:" + text);
System.out.println("-------------第二个例子,公钥加密私钥解密-------------");
// 公钥加密
String textsr = "吃过啦!你吃了吗?O(∩_∩)O~";
String cipherText = RSAUtil.encryptByPublicKey(textsr, RSAUtil.publicKey, Cipher.ENCRYPT_MODE);
System.out.println("接收方用公钥加密后:" + cipherText);
// 私钥解密
String text = RSAUtil.encryptByprivateKey(cipherText, RSAUtil.privateKey, Cipher.DECRYPT_MODE);
System.out.print("发送方用私钥解密后:" + text);
}
}
@RestController
public class PayController extends BaseApiService {
@Autowired
private BaseRedisService baseRedisService;
private static long timeToken = 15 * 60l;
@RequestMapping("/pay")
public ResponseBase pay(String token) {
// 获取提交参数 数据库保存.,
if (StringUtils.isEmpty(token)) {
return setResultError("token 不能为空!");
}
String reuslt = (String) baseRedisService.getString(token);
if (StringUtils.isEmpty(reuslt)) {
return setResultError("参数不能空!");
}
System.out.println("获取提交的参数reuslt:" + reuslt);
return setResultSuccess("获取提交的参数reuslt:" + reuslt);
}
@RequestMapping("/getPayToken")
public String pay(Long userId, Long money) {
String payToken = UUID.randomUUID().toString();
baseRedisService.setString(payToken, userId + "-" + money, timeToken);
return payToken;
}
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name www.haoworld.cn;
location /api-a {
proxy_pass http://127.0.0.1:8000/;
index index.html index.htm;
}
location /api-b {
proxy_pass http://127.0.0.1:8001/;
index index.html index.htm;
}
}
}
创建项目eureka_server
eureka_server pom依赖信息
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<!-- 管理依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- 注意: 这里必须要添加, 否者各种依赖有问题 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
application.yml 配置信息
server:
port: 8100
eureka:
instance:
hostname: server1
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
register-with-eureka: false
fetch-registry: false
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(final String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class AIndexController {
@RequestMapping("/")
public String index() {
return "我是A项目....";
}
public static void main(String[] args) {
SpringApplication.run(AIndexController.class, args);
}
}
server:
port: 8000
spring:
application:
name: itmayiedu_a
eureka:
client:
service-url:
defaultZone: http://localhost:8100/eureka
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class AIndexController {
@RequestMapping("/")
public String index() {
return "我是B项目....";
}
public static void main(String[] args) {
SpringApplication.run(AIndexController.class, args);
}
}
server:
port: 8000
spring:
application:
name: itmayiedu_a
eureka:
client:
service-url:
defaultZone: http://localhost:8100/eureka
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8100/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: haoworld_a
api-b:
path: /api-b/**
serviceId: haoworld_b
@EnableZuulProxy
@EnableEurekaClient
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
@Bean
public TokenFilter accessFilter() {
return new TokenFilter();
}
}
// 使用网关拦截Token参数
public class TokenFilter extends ZuulFilter {
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
Object accessToken = request.getParameter("accessToken");
if (accessToken == null) {
// 返回错误信息
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
ctx.setResponseBody("accessToken is null");
return null;
}
return null;
}
public boolean shouldFilter() {
return true;// 是否执行该过滤器,此处为true,说明需要过滤
}
@Override
public int filterOrder() {
return 0;// 优先级为0,数字越大,优先级越低
}
@Override
public String filterType() {
return "pre"; // 前置过滤器
}
}
// 自定义重定向
@RequestMapping("/customRedirection")
public void customRedirection(HttpServletResponse response) {
// 告诉给客户端重定向
response.setStatus(302);
response.setHeader("location", "loginPage");
return;
}
// 重定向到登陆页面
@RequestMapping("/redirectloginPage")
public String redirectLogin() {
return "redirect:/loginPage";
}
// 首页访问
@Controller
@SpringBootApplication
public class IndexController {
// 登陆页面
@RequestMapping("/loginPage")
public String loginPage() {
return "loginPage";
}
// 登陆返回json格式
@PostMapping(value = "/loginJSON")
@ResponseBody
public String login(String userName, String passWord) {
System.out.println("userName:" + userName + ",passWord:" + passWord);
return "userName:" + userName + ",passWord:" + passWord;
}
public static void main(String[] args) {
SpringApplication.run(IndexController.class, args);
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登陆</title>
</head>
<body>
<form action="loginJSON" method="post">
<span> 用户名称:</span><input type="text" value="" name="userName"><br>
<span>用户密码:</span><input type="password" name="passWord" value=""><br>
<input type="submit" value="提交"><br>
</form>
</body>
</html>
修改Http请求基本信息
4)浏览器收到消息后解密成功,则握手结束,后续的信息都通过此随机密钥加密传输。
以上是服务端认证的情况,如果服务端对访问的客户端也有认证需求,则客户端也需要将自己的证书发送给服务器,服务器认证不通过,通讯结束;原理同上;
另外,一般在传输过程中为了防止消息窜改,还会采用消息摘要后再加密的方式,以此保证消息传递的正确性
总结
1.客户端发送自己支持的加密规则给服务器,代表告诉服务器要进行连接了
2.服务器从中选出一套加密算法和hash算法以及自己的身份信息(地址等)以证书的形式发送给浏览器,证书中包含服务器信息,加密公钥,证书的办法机构
3.客户端收到网站的证书之后要做下面的事情:
4.服务器接收到客户端传送来的信息,要求下面的事情:
原文:https://www.cnblogs.com/haoworld/p/distributed-hu-lian-wang-an-quan-jia-gou.html