实验用到的模板
https://download.csdn.net/download/m2606707610/12122449
已上传,免费下载。
在上一篇博客中我已经在HelloWorld中导入了该模板,大家可以参考上篇博客,来进行这篇博客的学习。
https://blog.csdn.net/m2606707610/article/details/104099005
第三章 SpringSecurity-实验
1.实验一:授权首页和静态资源
配置类(AppWebSecurityConfig extends WebSecurityConfigurerAdapter)
重写configure(HttpSecurity http)方法
@Configuration @EnableWebSecurity public class AppWebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); //取消默认配置 http.authorizeRequests() .antMatchers("/layui/**","/index.jsp").permitAll() //设置匹配的资源放行 .anyRequest().authenticated(); //剩余任何资源必须认证 } } |
测试结果
- 静态资源和index.jsp都可以访问
- 不存在的资源或有权限但不存在的资源404
3.无权限的资源,403访问被拒绝
2.实验二:默认及自定义登录页
- 开启formLogin()功能 ,http.formLogin()
- 静态资源和index.jsp都可以访问
- 不存在的资源
http://localhost/spring-security-helloworld/layui/xxx 有权限时,找不到资源会报404错误
- 总结:默认表单登录页面的规则
1)、自动生成一个登录页
2)、登录请求被默认提交到 /login POST下
3)、生成隐藏域,可以防重复提交和跨站请求伪造;
<input name="_csrf" type="hidden" value="755f0b3c-0965-430b-852e-dcf6c77e7edb" />
为了测试方便,先禁用这个功能: http.csrf().disable();
4)、默认提交的字段名为:name='password' name='username'
- 指定登录页
- http.formLogin().loginPage("/index.jsp"); //去到指定的登录页
- 静态资源和index.jsp都可以访问
- 不存在的资源
http.formLogin()//开启表单登录功能
.loginPage("/toLogin")//自定义默认登录页,这里toLogin为转向自己的登录页
.usernameParameter("loginacct")//这里指定自定义登录页的账号name
.passwordParameter("userpswd")//这里指定自定义登录页的密码name
.loginProcessingUrl("/doLogin")//这指定的是自定义登录页表单提交的action,不指定则默认为/login
.defaultSuccessUrl("/main");//登陆成功后去到哪个页面
3.3 实验三:自定义表单登录逻辑分析
- 表单提交地址:${PATH }/index.jsp
- 表单提交请求方式:post
- 表单提交请求失败,提取错误消息:${SPRING_SECURITY_LAST_EXCEPTION.message}
- 如何提交表单:
- 引入jquery: <script src="${PATH }/layui/jquery.min.js"></script>
- $("form").submit();
- 表单提交参数名称: username password
- 提交请求被拒绝
暂时禁用csrf:http.csrf().disable();
- 登录逻辑分析
/**默认登录页面 * /login GET - the login form * /login POST - process the credentials and if valid authenticate the user * /login?error GET - redirect here for failed authentication attempts * /login?logout GET - redirect here after successfully logging out
* 定制登录页面:loginPage("/index.jsp"):规定登录页的地址在哪里 * /index.jsp GET - the login form * /index.jsp POST - process the credentials and if valid authenticate the user * /index.jsp?error GET - redirect here for failed authentication attempts * /index.jsp?logout GET - redirect here after successfully logging out * ${SPRING_SECURITY_LAST_EXCEPTION.message}可以取出错误消息 */ |
- 测试结果
3.4实验四:自定义认证用户信息
- 自定义认证用户信息
|
- CSRF跨站请求伪造
- SpringSecurity添加了csrf功能【DefaultCsrfToken】,所有的表单提交为了防止跨站请求伪造,我们需要加上_csrf项; 或者,暂时禁用http.csrf().disable();
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
-
- ${_csrf} ===>>> org.springframework.security.web.csrf.DefaultCsrfToken@19116cfd
- <input type="hidden" name="_csrf" value="310988c2-3f9d-4651-9e19-6ef4b2c4aa3a"/>
- 如果不禁用csrf,默认是开启的状态;页面不设置csrf表单域,那么,提交登录请求会报错
- 令牌值变化:
- 如果登录成功(用户名,密码正确),令牌会被删除,
- 重新回到登录页或后退网页,令牌会重新生成;
- 如果登录失败(用户名,密码错误),令牌不变。
- 刷新登录页,令牌值也不变
- 作用:
- 防止别的网站伪造数据,提交请求到我们的网站。
- 扩展-了解XSS
- XSS攻击全称跨站脚本攻击,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
- CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。
3.5实验五:用户注销完成
- 添加注销功能(logout)http.logout()默认规则
- /logout:退出系统
- 如果csrf开启,必须post方式的/logout请求,表单中需要增加csrf token
- logoutUrl();退出系统需要发送的请求,默认为/logout
- logoutSuccessUrl();退出系统成功以后要跳转的页面地址
- addLogoutHandler():自定义注销处理器
- deleteCookies():指定需要删除的cookie
- invalidateHttpSession():session失效(DEBUG)
3.6 实验六:基于角色的访问控制
- 设置资源可以访问的角色
http.authorizeRequests().antMatchers("/layui/**","/index.jsp").permitAll() //允许所有人都访问静态资源,无论登录(认证)与否 .antMatchers("/level1/**").hasRole("学徒") .antMatchers("/level2/**").hasRole("大师") .antMatchers("/level3/**").hasRole("宗师") .anyRequest().authenticated(); //放置最后,以上没有规定的都需要权限认证。 |
- 注意:
- 将.anyRequest().authenticated()错误的设置在前面,后面的设置就不起作用了。
- 设置所有,"/**"都可以访问,其他再进行的设置就不会起作用了
- 设置匿名访问/level3/** 可以不用登录,匿名访问:.anyRequest().anonymous();
- 拥有该角色的资源可以访问,否则不可以访问
auth.inMemoryAuthentication() .withUser("zhangsan").password("123456").roles("ADMIN","学徒","宗师") .and() .withUser("自定义访问拒绝处理页面,lisi").password("111111").authorities("USER","MANGER"); |
3.7 实验七:自定义访问拒绝处理页面
- 直接增加处理映射界面
http.exceptionHandling().accessDeniedPage("/unauth.html"); |
- 在控制器类中增加映射处理
@RequestMapping("/unauth.html") public String unauth(){ return "unauth"; } |
- 增加显示页面,将main.jsp复制,命名为unauth.jsp,增加一句提示信息
<h1>你无权访问该页面...</h1> |
- 测试显示效果
- 自定义异常处理器
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { request.setAttribute("message", accessDeniedException.getMessage()); request.getRequestDispatcher("/WEB-INF/views/unauth.jsp").forward(request, response); } }); |
3.8 实验八:记住我功能
3.8.1 记住我功能-免登录原理
- http.rememberMe();
- 默认规则
- 页面checkbox提交remember-me参数
- 默认记住2周登录状态:AbstractRememberMeServices
-
- 会在cookie中保存名为:remember-me的cookie
- 记住了以前登录的状态,以后再访问就不用登录了
- 登录后页面,关闭浏览器,直接访问:
http://localhost/spring-security-helloworld/main.html 可以成功访问,不必登录。
- 这种方式,token值是放置在内存中的,服务器端重启tomcat,token会失效。需要将token记录在数据库持久化才不会失效。
3.8.2 记住我-数据版
- 引入pom.xml 包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.20.RELEASE</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> |
- 配置数据源
<!-- 配置数据源 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="root"></property> <property name="password" value="root"></property> <property name="url" value="jdbc:mysql://192.168.137.3:3306/security?useSSL=false"></property> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> </bean> <!-- jdbcTemplate--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> |
- 创建表
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key,token varchar(64) not null, last_used timestamp not null) |
- 设置记住我
@Autowired DataSource dataSource;
@Override protected void configure(HttpSecurity http) throws Exception { //。。。 //记住我 JdbcTokenRepositoryImpl ptr = new JdbcTokenRepositoryImpl(); ptr.setDataSource(dataSource); http.rememberMe().tokenRepository(ptr); } |