Security自动登录与防CSRF攻击冲突解决办法

项目场景:

使用技术,后端srpingboot+security,前端Vue+elementui+axios

问题描述:

security有两个强大功能之二:自动登录和防CSRF攻击。防CSRF攻击的功能说白了,就是多加一个token验证。然而这两个功能同时开启时,会出现POST/PUT请求无访问权限。

原因分析:

这里的问题原因就是token,对于一次建立连接,CRFS的token是唯一的,但是自动登录跳过了登录环节,因此CRFS的token值是重新生成的,也就意味着之前保存的token无效了。因此只需要重新获取保存即可。
这里遇到一个重大问题:就是当自动登录时,直接进入的主页面,但是在主页面下有多个异步请求获取数据,这就会多次发送token值。会出现时而有效时而无效的问题。

解决方案:

经过测试,如果登录时只发送一次请求,之后再发送其他异步请求,就能保证只有一个session会话。也就能保证只发送一个token。这样即可保证之后发送的异步请求使用的是同一个token。 因此这里需要多加一次请求,这个请求可以什么也不做,只保证必须第一次请求。因此最好放在App.vue的beforeCreate函数中。如下所示:
export default {
	name: 'app',
	beforeCreate() {
		// 这里的axios已封装在window中,因此能够保证调用的到
		// 解决 免登陆 CRFS问题
		var xmlhttp;
		if (window.XMLHttpRequest) { // code for IE7+, Firefox, Chrome, Opera, Safari
			xmlhttp = new XMLHttpRequest();
		} else { // code for IE6, IE5
			xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
		};
		if (!xmlhttp) {
			console.log("不支持xml发送")
			return;
		};
		// 发送同步请求,保证成功之后再去请求其他异步请求
		xmlhttp.open("get", process.env.VUE_APP_PROXYNAME + "/ok?t=" + Math.random(), false);
	}
}

后端首先保证在登录成功后做一次token发送,保存在cookie中,保证不需要在前端有任何操作

/**
 * 登录成功回调
 */
public class LoginSuccessResponse extends SimpleUrlAuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        /**
         * 获取CSRF密钥
         *   "csrf": {
         *       "headerName": "X-CSRF-TOKEN",  必须是这个作为key
         *       "parameterName": "_csrf",
         *       "token": ""
         *   },
         *   向前端发送密钥,每次请求写在头信息中,
         *   例如 X-CSRF-TOKEN: 'token',即可保证正常访问
         *   使用Cookie保证前端不需要做任何操作
         */
        CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        // 这里使用fastjson转成JSON格式
        Cookie cookie = new Cookie("csrf", URLEncoder.encode(JSON.toJSONString(csrfToken),"UTF-8"));
        // 保存10天,保证存活时间足够使用
        cookie.setMaxAge(60 * 60 * 24 * 10);
        cookie.setPath("/");
        response.addCookie(cookie);
        // sendCsrfToken记录boolean值,表示在一次浏览器会话中,保证发送一次toekn值
        request.getSession().setAttribute("sendCsrfToken", true);
        out.flush();
        out.close();
    }
}

接下来就是配置请求/ok,保证自动登录情况下也能正常访问

/**
 * 公共请求
 */
@Controller
@Slf4j
public class CommonController {

    /**
     * 解决免登陆情况下CRFS验证问题
     * @param request
     * @param response
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/ok")
    @ResponseBody
    public void ok(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        Object sendCsrfToken = request.getSession().getAttribute("sendCsrfToken");
        if (sendCsrfToken == null || (boolean) sendCsrfToken == false) {
            CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
            Cookie cookie = new Cookie("csrf", URLEncoder.encode(JSON.toJSONString(csrfToken),"UTF-8"));
            cookie.setMaxAge(60 * 60 * 24 * 10);
            cookie.setPath("/");
            response.addCookie(cookie);
            log.info("免登陆用户{},发送csrfToken:{}", SecurityContextHolder.getContext().getAuthentication().getName(),csrfToken.getToken());
            request.getSession().setAttribute("sendCsrfToken", true);
        }
    }
}

配置之后即可保证在cookie中总会有一个有效的crsftoken值,只需要在每次请求拦截前配置请求头即可正常访问。如下:

_axios.interceptors.request.use(
	function(config) {
		let cookie = document.cookie;
		if (cookie) {
			// 这里使用vue-cookies插件,也可以直接从cookie中获取数据
			let csrf = VueCookies.get("csrf ")
			if (csrf) {
				config.headers[csrf.headerName] = csrf['token'];
			}
		}
		// Do something before request is sent
		return config;
	},
	function(error) {
		// Do something with request error
		return Promise.reject(error);
	}
);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CSRF(Cross-Site Request Forgery)攻击是一种常见的网络安全漏洞,它利用了网站对用户请求的信任,通过伪造用户的请求来执行恶意操作。 攻击原理: 1. 用户登录受信任的网站A,并在本地生成了相应的会话Cookie。 2. 攻击者诱使用户访问恶意网站B,该网站中包含了针对网站A的恶意请求。 3. 网站B的恶意请求会自动触发用户浏览器发送一个针对网站A的请求,由于用户在访问网站A时已经登录,并且浏览器会自动携带相应的会话Cookie。 4. 网站A接收到请求后,会认为是用户自己的合法请求,然后执行相应的操作,比如修改密码、发起转账等。 解决方法: 1. 验证码(CAPTCHA):引入验证码可以阻止CSRF攻击,因为攻击者无法获取到验证码的内容,无法伪造合法请求。 2. 同源检测:在服务器端对请求进行来源验证,只允许来自同一域名下的请求通过。可以通过检查Referer字段或者使用CSRF Token来实现。 3. CSRF Token:在每个表单或者请求中引入一个随机生成的Token,并在服务器端校验,如果请求中没有正确的Token,则拒绝执行操作。 4. 阻止第三方网站请求:可以通过设置HTTP头部的SameSite属性为Strict或者Lax来限制第三方网站对Cookie的访问。 5. 双重Cookie验证:除了验证会话Cookie,还可以在请求中包含一个随机生成的Token,并在服务器端进行验证。 以上是一些常见的CSRF攻击解决方法,但并不是绝对安全的,开发者在设计和开发过程中还需要综合考虑其他安全措施来确保网站的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

£漫步 云端彡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值