Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。
用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。
用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理! 记住几个类:
WebSecurityConfigurerAdapter:自定义Security策略 AuthenticationManagerBuilder:自定义认证策略 @EnableWebSecurity:开启WebSecurity模式 Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。 “认证”(Authentication) 身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。 身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。 “授权” (Authorization) 授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。 这个概念是通用的,而不是只在Spring Security 中存在。
导入需要的pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
修改配置文件:
后缀改为yml格式:
server:
port: 80
spring:
thymeleaf:
cache: false
创建一个Controller:
@Controller
public class TestController {
@RequestMapping({"/", "/index"})
public String index(){
return "index";
}
}
前端index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试界面</title>
</head>
<body>
<h1>这里是首页、</h1>
</body>
</html>
启动项目:
控制台会默认生成一个密码:访问:http://localhost/login
出现如下界面:
这个界面因为会访问外网的一个css所以会加载很慢,我们去项目里面添加配置:
自定义编写一个登录界面:
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<div style="font-size: 20px;">
<form th:action="@{/login}" method="post">
用户名:<input type="text" name="username" /><br />
密码:<input type="password" name="password" /><br />
<input type="submit" value="登录" />
</form>
</div>
</body>
</html>
编写一个controlle跳转到login界面
@GetMapping("/login")
public String login(){
return "login";
}
重庆后访问:http://localhost/
还是默认访问到security自带的登录界面:
现在就需要我们去配置默认的请求地址了
创建一个配置文件类:
SecurityConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity // 可以省略,在底层自动帮我们配置了这个注解
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 配置基本的拦截器
http.authorizeHttpRequests(authorize -> {
authorize.requestMatchers("/login").permitAll() // 放行登录界面
.anyRequest() // 对所有请求开启授权保护
.authenticated() // 已认证的请求会被自动授权
;
});
http.formLogin(login -> {
login.loginPage("/login") // 自定义登录地址
.successForwardUrl("/") // 登录成功,自动跳转到首页
;
});
return http.build();
}
}
再次访问:
自动跳转到登录界面:
现在输入用户名和密码:
用户名为:user (默认的)
密码为:控制台自动输出的密码:
登录成功,返回到首页
只需要在配置文件中加入一下配置:
server:
port: 80
spring:
thymeleaf:
cache: false
security:
user:
name: huaijiu
password: 123
重启,再次登录:
登录成功!
1.首先导入需要的静态资源: 链接:网页链接 提取码:clsq
1、将下载的配置导入到项目中:
2、重启项目测试一下:访问界面:http://localhost/ 并登录用户
登录成功,进入首页!
访问首页的level界面:
报错404,原因没有配置对应的controlle
3、添加对应映射路径
添加映射:
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") String id){
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") String id){
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") String id){
return "views/level3/" + id;
}
添加完成-重启项目进行访问:
这里的可以随意进行访问
3、添加配置文件内容
配置需要拥有对应权限才可以访问对应资源
// 配置基本的拦截器
http.authorizeHttpRequests(authorize -> {
authorize.requestMatchers("/login").permitAll() // 放行登录界面
.requestMatchers("/level1/**").hasAnyRole("vip1") // 配置对应请求,需要对应权限才可以访问
.requestMatchers("/level2/**").hasAnyRole("vip2")
.requestMatchers("/level3/**").hasAnyRole("vip3")
.anyRequest() // 对所有请求开启授权保护
.authenticated() // 已认证的请求会被自动授权
;
});
重启项目,访问测试:
报错:403 没有访问权限
添加配置文件:
@Bean
public UserDetailsService userDetailsService(){
// 1.使用内存数据进行认证
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// 2.创建三个用户
UserDetails user1 = User.withDefaultPasswordEncoder().username("h1").password("123").roles("vip1").build();
UserDetails user2 = User.withDefaultPasswordEncoder().username("h2").password("123").roles("vip2", "vip3").build();
UserDetails user3 = User.withDefaultPasswordEncoder().username("h3").password("123").roles("vip1", "vip2", "vip3").build();
// 3.将这三个用户添加到内存中
manager.createUser(user1);
manager.createUser(user2);
manager.createUser(user3);
return manager;
}
登录用户h1
访问:http://localhost/level1/1
访问成功:因为登录的hj1用户vip的权限,可以访问level1下的所有资源 测试访问其他界面:
报错!原因:没有对应访问权限!
模拟Dao层,查询用户数据:
编写User实体类: MyUser.java
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyUser {
private String username; // 用户名
private String password; // 密码
private String[] rol; // 权限
}
创建Dao: MyUserDao.java
public class MyUserDao {
public static List<MyUser> userList;
static {
userList = new ArrayList<>();
userList.add(new MyUser("h1", "123", new String[]{"vip1"}));
userList.add(new MyUser("h2", "123", new String[]{"vip2", "vip3"}));
userList.add(new MyUser("h3", "123", new String[]{"vip1", "vip2", "vip3"}));
}
public static List<MyUser> getList(){
return userList;
}
public static MyUser getUserByName(String name){
for (MyUser myUser : userList) {
if(myUser.getUsername().equals(name)){
return myUser;
}
}
return null;
}
}
创建一个DBUserDetailsManager.java类 实现 UserDetailsManager 和 UserDetailsPasswordService 两个接口
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUser user = MyUserDao.getUserByName(username);
if(user == null){
throw new UsernameNotFoundException(username);
}
// 创建权限列表
Collection<GrantedAuthority> authorities = new ArrayList<>();
for (String s : user.getRol()) {
authorities.add(() -> s);
}
return new User(user.getUsername(), // 用户名
new BCryptPasswordEncoder().encode(user.getPassword()), // 密码
true, // 是否启用
true, // 账号是否过期
true, // 用户凭证是否过期
true, // 用户是否未被锁定
authorities // 用户的权限列表
);
}
修改配置文件:
@Bean
public UserDetailsService userDetailsService(){
DBUserDetailsManager manager = new DBUserDetailsManager();
return manager;
}
修改配置认证代码:
// 配置基本的拦截器
http.authorizeHttpRequests(authorize -> {
authorize.requestMatchers("/login").permitAll() // 放行登录界面
.requestMatchers("/level1/**").hasAnyAuthority("vip1") // 配置对应请求,需要对应权限才可以访问
.requestMatchers("/level2/**").hasAnyAuthority("vip2")
.requestMatchers("/level3/**").hasAnyAuthority("vip3")
.anyRequest() // 对所有请求开启授权保护
.authenticated() // 已认证的请求会被自动授权
;
});
重启运行项目:
登录h1的用户,访问vip1资源
访问成功!
测试vip2权限
报错,没权限
注销掉基于配置是授权代码:
开启方法授权的配置,在配置类最上面添加注解:@EnableMethodSecurity
给方法添加授权注解:@PreAuthorize("hasAuthority('vip1')")
@RequestMapping("/level1/{id}")
@PreAuthorize("hasAuthority('vip1')")
public String level1(@PathVariable("id") String id){
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
@PreAuthorize("hasAuthority('vip2')")
public String level2(@PathVariable("id") String id){
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
@PreAuthorize("hasAuthority('vip3')")
public String level3(@PathVariable("id") String id){
return "views/level3/" + id;
}
使用注解后即可完成配置!
添加支持记住我功能配置
// 开启记住我配置:
http.rememberMe(remember -> {
remember.tokenValiditySeconds(60 * 60 * 24 * 7) // 有效时间:单位s 默认为14天
.rememberMeParameter("jizhuwo") // 前端提交name的名称对应
.rememberMeCookieName("jizhuwo") // cookies记录中的名字
;
});
编写前端登录界面:添加选项 (和上面的配置对应-要是上面的配置没编写,可以使用默认的remember-me)
<input type="checkbox" name="jizhuwo" value="true" />记住我<br />
添加配置文件
// 注销的配置
http.logout(logout -> {
logout.logoutUrl("/logout") // 注销的请求地址
.invalidateHttpSession(true) // 清除session信息,默认true
.clearAuthentication(true) // 清除认证信息,默认true
.logoutSuccessUrl("/login").permitAll(); // 注销成功后,跳转的位置
});
//关闭跨域访问,解决注销404问题
http.csrf(csrf -> {
csrf.disable();
});
修改添加注销按钮:(首页--index.html界面)
<!--登录注销-->
<div class="right menu">
<a class="item" th:href="@{/logout}">
<i class="address card icon"></i> 注销
</a>
</div>
点击注销即可完成注销!
在前端index界面导入 命名空间
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6"
在每个请求位置添加如下配置:
sec:authorize="hasRole('vip1')"
配置完成启动项目:登录用户h1
登录用户h2:
达成效果:
添加判断,如果用户登录就显示用户的用户名:
直接添加代码:
<!--如果已登录-->
<div class="right menu" sec:authorize="isAuthenticated()"> <!-- 判断是够已经登录 -->
<a class="item">
<i class="address card icon"></i>
用户名:<span sec:authentication="principal.username"></span>
</a>
</div>
评论
登录后才可以进行评论哦!