写在前面

上一回我们简单分析了spring security拦截器链的加载流程,我们还有一些简单的问题没有解决。如何自定义登录页面?如何通过数据库获取用户权限信息?

今天主要解决如何配置自定义认证页面的问题。因为现在前后端分离,无状态、restful接口设计比较火,因此在思考静态网页如何获取spring security的CRSF Token.这个问题我在文末提出了我的见解,但似乎也不是很好的解决方案,很期待大家的宝贵建议!

Spring Security配置自定义认证页面步骤

第一步:在spring security的配置文件中指定自定义登录页面的信息

<!--静态资源不拦截-->
<security:http pattern="/assets/**" security="none"/>
<!--设置可以用spring的el表达式配置Spring Security并自动生成对应配置组件(过滤器)-->
<security:http auto-config="true" use-expressions="true">
<!--使用spring的el表达式来指定项目所有资源访问都必须有ROLE_USER或ROLE_ADMIN角色-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')"/> <!-- 使这个页面能匿名访问-->
<security:intercept-url pattern="/login.html" access="permitAll()"/>
<security:form-login
login-page="/login.html"
login-processing-url="/login"
default-target-url="/pages/index.html"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/failure.html"/>
<security:logout
logout-url="/logout"
logout-success-url=":/login.html"/>
</security:http>

关于配置文件的几点说明:

  1. login-page:配置自定义的认证页面;
  2. login-processing-url:配置认证处理的url;
  3. default-target-url:配置认证成功之后跳转的页面;
  4. authentication-failure-url:配置认证失败之后跳转的页面;
  5. logout-url:配置注销处理路径;
  6. logout-success-url:配置成功注销后跳转的页面;
  7. username-parameter:配置前端表单向后台提交用户账户的参数名,默认值为username,可以不配置;
  8. password-parameter:配置前端表单向后台提交用户凭证的参数名,默认值为password。
  9. security="none"表示该路径下的资源访问不拦截(这个和匿名访问的配置是有区别的)

第二步:编写一个前端登录页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.13.2/lib/theme-chalk/index.css">
<style>
* {margin: 0; padding: 0; box-sizing: border-box;}
.form {position: relative;}
.son {position: relative; top:50%;margin-top:-50px;left:50%;margin-left:-50px}
</style>
<body>
<div id="app">
<el-container>
<el-header></el-header>
<el-main style="margin-top: 10%"> <div id="form" style="width: 310px;height:280px;margin: auto;background-color: #71d3bd"> <el-form id="son" ref="form" :model="user" style="margin: auto;padding-left: 10px;padding-right: 10px">
<el-form-item>
<h3 style="text-align: center">欢迎登录</h3>
</el-form-item>
<el-form-item style="width: auto;">
<el-input v-model="user.username" placeholder="username"></el-input>
</el-form-item>
<el-form-item style="width: auto;">
<el-input v-model="user.password" placeholder="password"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" style="width:100%;">登录</el-button>
</el-form-item>
</el-form>
</div>
</el-main>
</el-container>
</div> <!--axios-->
<script src="https://cdn.bootcss.com/axios/0.19.0/axios.min.js"></script>
<!--vue-->
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script> <script> new Vue({
el: "#app",
data: function (){
return {
user:{
username:"",
password:""
}
}
},
methods:{
onSubmit(){
axios.post("/login",this.user)
}
} })
</script>
</body>
</html>

第三步,启动tomcat,访问http://127.0.0.1:8081/

我们可以发现,我们被重定向到了登录页面。接下来我们填写username和password点击登录(在前面的配置文件中定义了)

我们发现结果并不符合我们的预期。我们得到了403(没有权限)错误信息。

why?

我们对比一下我们写的登录页面和spring security给出的页面有啥不一样

<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Please sign in</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous">
<style>@media print {#ghostery-purple-box {display:none !important}}</style></head>
<body>
<div class="container">
<form class="form-signin" method="post" action="/login">
<h2 class="form-signin-heading">Please sign in</h2>
<p>
<label for="username" class="sr-only">Username</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required="" autofocus="">
</p>
<p>
<label for="password" class="sr-only">Password</label>
<input type="password" id="password" name="password" class="form-control" placeholder="Password" required="">
</p>
<input name="_csrf" type="hidden" value="0aef3732-50d3-455a-8404-43d5ed9f2939">
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</body></html>

对比发现系统给出的和我们写的认证页面在表单中多出了下面这段

<input name="_csrf" type="hidden" value="0aef3732-50d3-455a-8404-43d5ed9f2939">

也就是说我们少了一个参数信息,这参数信息就是我们前篇文章讲到的spring security预防csrf攻击的内容。显然它是通过校验token去预防的。

解释一下如何通过检验token的方式预防csrf攻击:

​ 简单来说就是赵六你去账房领银两办事(获取系统服务)前先向王员外(可以是系统专门发放令牌的一个接口)索要一个令牌(token),你领银两的时候带上王员外给你的令牌,这账房管事的李老先生(一个拦截器)查看了你的令牌后才能给你钱,不然就不给你钱。你拿钱了之后呢令牌就被收走了(token过期策略),下次再想拿钱就得找王员外再要一个新的令牌。

接下来就让我们找李老先生拿银两去,哦,不对,是找王员外拿鸡毛令箭去。。。

第四步:配置csrf攻击防护机制

从Spring Security 4.0开始,默认情况下使用XML配置启用CSRF保护。如果要禁用CSRF保护,可以在下面看到相应的XML配置。

<http>
<!-- ... -->
<csrf disabled="true"/>
</http>

由于spring security的csrf令牌是存储再HttpSession中的,因此在动态网页中,可以很方便的获取到csrf token信息。

<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:c="http://java.sun.com/jsp/jstl/core"
xmlns:form="http://www.springframework.org/tags/form" version="2.0">
<jsp:directive.page language="java" contentType="text/html" />
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<!-- ... --> <c:url var="logoutUrl" value="/logout"/>
<form:form action="${logoutUrl}"
method="post">
<input type="submit"
value="Log out" />
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
</form:form> <!-- ... -->
</html>
</jsp:root>

如果使用的是静态网页,例如前后端分离、restful接口设计和无状态session的情况下,或许可以考虑如下设计:

​ 将csef token暴露出去,前端通过ajax请求获取token,并在请求服务时带上token。当然,这里暴露出去的token应该是只能在内部暴露,使得静态页面得到token后,再将网页响应给用户。

获取token

登录

静态网页获取csrf token的部分代码

/**
* @author 赖柄沣 bingfengdev@aliyun.com
* @version 1.0
* @date 2020/8/21 12:29
*/
@RestController
@RequestMapping("/csrf")
public class CsrfTokenCtrl { @GetMapping(value = "/getToken")
public HashMap<String, String> getToken(HttpServletRequest request ){
/**
* 在这里可以先过滤掉一些非法的请求,只允许内部静态网页服务器请求
*/ CsrfToken token = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
HashMap<String, String> map = new HashMap<>();
map.put("csrf_header",token.getHeaderName());
map.put("csrf",token.getToken());
return map;
} }

End

为了保证系统安全,牺牲一些设计上的完美,在我看来是很有必要的。相信会有更好的方案!以上只是我个人的一些见解。欢迎大家提出不一样的想法。

Spring Security自定义认证页面(动态网页解决方案+静态网页解决方案)--练气中期圆满的更多相关文章

  1. (二)spring Security 自定义登录页面与校验用户

    文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...

  2. Spring Security 自定义登录页面

    SpringMVC + Spring Security,自定义登录页面登录验证 学习参考:http://www.mkyong.com/spring-security/spring-security-f ...

  3. Spring Security自定义认证器

    在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了 Authentication 首先要创建一个自 ...

  4. Spring Security 接口认证鉴权入门实践指南

    目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...

  5. spring security自定义指南

    序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...

  6. Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理

    本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...

  7. Spring Security 自定义登录认证(二)

    一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...

  8. 最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...

  9. 解决Spring Security自定义filter重复执行问题

    今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...

随机推荐

  1. R语言基本绘图-plot参数:标题,坐标轴和颜色

    标题 plot(c(1:2,2:4),main = "这是主标题",sub = "这是副标题",xlab = "这是x轴", ylab = ...

  2. time strptime()方法 时间操作

    Python time strptime()方法 时间操作   描述 Python time strptime() 函数根据指定的格式把一个时间字符串解析为时间元组. 语法 strptime()方法语 ...

  3. 在CentOS 7 上为docker配置端口转发以兼容firewall

    在CentOS 7上当我们以类似下列命令将主机端口与容器端口映射时可能遇到无法访问容器服务的问题 docker run --name web_a -p 192.168.1.250:803:80 -d ...

  4. 图解HTTP 6/11

    第三章 HTTP报文内的HTTP信息 1.用于http协议交互的信息被称为HTTP报文.请求端(客户端)的HTTP报文叫做请求报文,响应端(服务器端 )的叫做响应报文. 2.请求报文的结构 请求行:包 ...

  5. Python自动化运维:技术与最佳实践 PDF高清完整版|网盘下载内附地址提取码|

    内容简介: <Python自动化运维:技术与最佳实践>一书在中国运维领域将有“划时代”的重要意义:一方面,这是国内第一本从纵.深和实践角度探讨Python在运维领域应用的著作:一方面本书的 ...

  6. luogu P2973 [USACO10HOL]Driving Out the Piggies G 驱逐猪猡

    luogu LINK:驱逐猪猡 bzoj LINK:猪猪快跑 问题是在1时刻有个炸蛋在1号点 这个炸弹有p/q的概率爆炸 如果没有爆炸 那么会有1/di的概率选择一条边跳到另外一个点上重复这个过程. ...

  7. Jdbc与Dao和Javabean的区别

    JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口 ...

  8. cobbler多机定制安装

    目录 cobbler多机定制安装 1. cobbler服务端部署 2. 客户端安装 3. 定制安装配置 4. 安装 client1开机 client2开机 cobbler多机定制安装 1. cobbl ...

  9. SpringBoot之Quartz实战

    说明:由于上篇文章我们已经讨论过springboot整合Quartz及相关配置,本次我们只说明Qrtz的增.删.改.启动.停止相关api的使用,其中涉及的其他技术,如:mybatisplus等技术以后 ...

  10. Python | 常见的反爬及解决方法,值得收藏

    我们都知道Python用来爬数据,为了不让自家的数据被别人随意的爬走,你知道怎么反爬吗?今天播妞带着大家一起见识见识常见的反爬技术. 很多人学习python,不知道从何学起.很多人学习python,掌 ...