文章目录
一、简介
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架
。它是保护基于 Spring 的应用程序的事实上的标准。
Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架
。与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义要求。
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术类型
,它可以实现强大的Web安全控制。对于安全控制,我们仅需要引入spring-boot-starter-security模块
,进行少量的配置即可实现强大的安全跨管理。
有几个重要的类需要记住:
WebSecurityConfigurationAdapter:自定义Security策略(Web安全配置适配器)。
AuthenticationManagerBuilderr:自定义认证策略(身份验证管理器生成器)。
@EnableWebSecurity:开启WebSecurity模式(启用Web安全注解)。
spring Security的两个主要目标是“认证”(Authentication)和“授权/访问控制”(Authorization)
。这个概念四通用的,而不是在Spring Security中特有的。
参考文档1:https://docs.spring.io/spring-security/site/docs/current/reference/html5/
参考文档2:https://docs.spring.io/springsecurity/site/docs/5.2.0.RELEASE/reference/htmlsingle/
二、使用SpringSecurity
注意:thymeleaf最高支持的springboot的版本为2.0.9,我们使用的是2.0.7版本的springboot,如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
1、导入依赖
security启动器依赖:springsecurity和thymeleaf可以集合使用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
springsecurity-thymeleaf依赖:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
2、基础环境搭建
需要的前端文件以及目录结构:
介绍一些indexPage.html中的一些特别之处。因为thymeleaf是可以与springsecurity进行交互的,可以实现一些个性化的设置,比如:如果登录了显示登录人的名字和注销按钮,如果没登录显示登录按钮。
xmlns:th=“http://www.thymeleaf.org”:thymeleaf标签
xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity4”:thymeleaf-springsecurity标签
sec:authorize:授权
isAuthenticated():判断是否已经认证
sec:authentication:认证
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>index page</title>
</head>
<body>
<div class="box">
<h2>【主页】
<!--判断是否登录,!isAuthenticated()为true则生效<span>-->
<span sec:authorize="!isAuthenticated()">
<a th:href="@{/login}">登录</a>
</span>
<!--判断是否登录,!isAuthenticated()为true则生效<span>-->
<span sec:authorize="isAuthenticated()">
用户名:<span sec:authentication="name"></span>
<a th:href="@{/logout}">注销</a>
</span>
</h2>
<br><br>
<a th:href="@{/admin}">进入管理员页面</a>
<br><br>
<a th:href="@{/user}">进入用户页面</a>
<br><br>
<a th:href="@{/tourist}">进入游客页面</a>
</div>
</body>
</html>
编写路由控制器类:
package com.tiger.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class routeController {
@RequestMapping("/admin")
public String admin(){
return "pages/admin/adminPage";
}
@RequestMapping("/user")
public String user(){
return "pages/user/userPage";
}
@RequestMapping("/tourist")
public String tourist(){
return "pages/tourist/touristPage";
}
@RequestMapping("/login")
public String login(){
return "pages/loginPage";
}
}
3、编写SecurityConfig配置类
基本结构:
package com.tiger.config;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
.......
}
①、重写configure(HttpSecurity http)方法
重写configure(HttpSecurity http)实现页面角色设置:
对于不同的页面,我们可能会有不同的权限,怎么给不同的页面设置不同的权限,在原生的Javaweb中,我们可能会用到过滤器(filter),但是在SpringSecurity中,我们只需要继承一个类就可以。
authorizeRequests():授权请求
antMatchers(String str):设置匹配url
hasRole(String str):设置角色
permitAll():允许所有的角色通过
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//设置页面角色(匹配器)
.antMatchers("/").permitAll()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.antMatchers("/tourist/**").hasRole("tourist");
}
重写configure(HttpSecurity http)实现登录与注销:
在SpringSecurity中实现登录与注销,变得异常的简单,不用在通过原本的javaweb进行繁琐的检验与判断。
formLogin():表单登录
loginPage("/login"):设置用于登录使用的页面
passwordParameter(“username”):接收表单中name=username的参数
passwordParameter(“password”):接收表单中name=password的参数
logoutSuccessUrl(String str):注销成功后的路径设置
@Override
protected void configure(HttpSecurity http) throws Exception {
//设置登录页面与专递的参数名
http.formLogin()
.loginPage("/login")
.passwordParameter("username")
.passwordParameter("password");
//设置注销
.and()
.logout().logoutSuccessUrl("/")
}
对于注销,有一个固定的访问路径<a th:href="@{/logout}">注销</a>
重写configure(HttpSecurity http)实现无权限的访问的重定向:
对于没有访问权限的用户访问了高权限的url。SpringSecurity会默认跳到一个错误提示页面,体验不好。通常我们都会让其跳到登录页面或者在当前页保持不变。
exceptionHandling():异常处理
accessDeniedPage(String str):访问拒接页面设置
@Override
protected void configure(HttpSecurity http) throws Exception {
//设置无权限的访问的默认访问路径
http.exceptionHandling().accessDeniedPage("/login");
}
重写configure(HttpSecurity http)实现登录时的"记住我":
我们在登录一些比较用心的网站的时候,在我们登录的时候,存在一个"记住我"的单选按钮,当我们勾选之后,即使我们关闭浏览器之后,我们依然可以免登陆访问。其实背后的后继也是很简单的,无非即使session或cookie的应用,只不过在SpringSecurity变得更加的简单了。
rememberMeParameter(String str):str的值要与checkbox的name值相对应,例如:<input type="checkbox" name="rememberMe" value="rememberMe">记住我 <br>
@Override
protected void configure(HttpSecurity http) throws Exception {
//设置“记住我”
http.and()
.rememberMe().rememberMeParameter("rememberMe");
}
重写configure(HttpSecurity http)设置csrf禁用
注销按钮要是点击时失败,那么说明你用的是< a>标签,< a>标签使用的是get请求。SpringSecurity中有csrf保护,csrf又称跨域请求伪造,攻击方通过伪造用户请求(get)访问受信任站点
。
所以我们可以通过表单的post请求
实现注销,或者禁用SpringSecurity的csrf保护
。
@Override
protected void configure(HttpSecurity http) throws Exception {
//设置csrf禁用
http.csrf().disable();
}
整合上述功能:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//设置页面角色
.antMatchers("/").permitAll()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.antMatchers("/tourist/**").hasRole("tourist")
//设置登录页面与专递的参数名
.and()
.formLogin()
.loginPage("/login")
.passwordParameter("username")
.passwordParameter("password")
//设置无权限访问页面的默认路径
.and()
.exceptionHandling().accessDeniedPage("/login")
//设置"记住我"
.and()
.rememberMe().rememberMeParameter("rememberMe")
//设置注销
.and()
.logout().logoutSuccessUrl("/")
//设置csrf禁用
.and()
.csrf().disable();
}
②、重写configure(AuthenticationManagerBuilder auth)方法
既然我们前面说了,每个页面都有对应的角色去访问,我们接下来就是给登录见证成功的用户分配角色。
重写configure(AuthenticationManagerBuilder auth)方法校验登录名与密码以及角色(权限)设置。
inMemoryAuthentication():在内存中校验
passwordEncoder(new BCryptPasswordEncoder()):密码编码,一种加密方式
//设置用户名和密码以及对应的角色
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("admin","user","tourist")
.and()
.withUser("user").password(new BCryptPasswordEncoder().encode("user")).roles("user","tourist")
.and()
.withUser("tourist").password(new BCryptPasswordEncoder().encode("tourist")).roles("tourist");
}
您可以找到支持基于 JDBC 的身份验证的更新。下面的示例假设您已经DataSource在应用程序中定义了 a 。该JDBC-javaconfig样品提供了使用基于JDBC认证的一个完整的示例。
@Autowired
private DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// ensure the passwords are encoded properly
UserBuilder users = User.withDefaultPasswordEncoder();
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser(users.username("user").password("password").roles("USER"))
.withUser(users.username("admin").password("password").roles("USER","ADMIN"));
}
未完待续…
天下无难事,只怕有心人。天下无易事,只怕粗心人…