文章目录
视频地址
https://www.bilibili.com/video/BV15a411A7kP?from=search&seid=9400151469496223964
资料地址
链接:https://pan.baidu.com/s/18NAnL40uxvmpPuLZXKM7uA 提取码:t209
入门测试
- 配置pom.xml文件
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<!-- web场景 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring的安全框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
- 写主启动类
@SpringBootApplication
public class SpringSecurityMainApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityMainApplication.class,args);
}
}
- 配置application.porperties文件 配置文件
# 设置端口为8080
server.port=8080
- 写controller
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("hello")
public String hello(){
return "hello Security";
}
}
直接run主启动类,访问 test/hello
页面跳转
用户名:user 密码
登录后:
设置用户名密码
方法一:通过配置文件
spring.security.user.name=atguigu
spring.security.user.password=atguigu
方法二:通过配置类
创建SecurityConfig配置类
package com.atguigu.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("123");
//将123 给加密了,下面的方法,是将 加密后的密码给解密为 123 跟 输入的密码进行匹配!!!
//role角色必须要写!!!
// 密码必须进行加密后,再进行传递进去,才行。
auth.inMemoryAuthentication().withUser("atguigu").password(password).roles("admin");
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
输入: atguigu 123 就可
方式三:自定义编写实现类(连接数据库使用)
先出配置文件中找用户名密码,没有从配置类中找,再没有就从这个自定义类中找,没有就默认的用户名密码
第一步:创建配置类。设置是使用哪个userDetaillsService实现类
注意:放在一个配置类中写即可。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired // 这个是一个接口,注入的是它的实现类
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 对应的实现类,使用BCryptPasswordEncoder 这个类进行加密的
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
第二步:编写实现类,返回User对象,User对象用户名密码和权限操作
package com.atguigu.service;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userDetailsService")
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//将用户对应的角色,封装为一个list集合,这里是使用了这个工具类
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 必须将密码加密后,才能进行放入
return new User("mary",new BCryptPasswordEncoder().encode("123"),auths);
}
}
输入: mary123 就可
案例:查询数据库完成认证
第一步:引入相关依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok 用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
第二步:创建数据库和数据库表
Users表
create table users(
id bigint primary key auto_increment,
username varchar(20) unique not null,
password varchar(100)
);
第三步:创建对应的实体类
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private Integer id;
private String username;
private String password;
}
第四步:整合mp,创建接口,继承mp接口
@Repository
public interface UsersMapper extends BaseMapper<Users> {
}
第五步:在MyUserDetailService调用mapper里面的方法查询数据库进行用户认证
package com.atguigu.service;
import com.atguigu.entity.Users;
import com.atguigu.mapper.UsersMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userDetailsService")
public class MyUserDetailService implements UserDetailsService {
@Autowired
UsersMapper usersMapper; //注意这里
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//这里的username指的是表单传过来的用户名的值
//调用userMapper方法,根据用户名查询数据库
QueryWrapper<Users> wrapper = new QueryWrapper<>();
//where usernanme = ?
wrapper.eq("username",username);
Users users = usersMapper.selectOne(wrapper);
//判断
if(users == null){
throw new UsernameNotFoundException("用户名不存在");
}
//将用户对应的角色,封装为一个list集合,这里是使用了这个工具类
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");// 没有设置用户的角色的
// 必须将密码加密后,才能进行放入
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
第六步:在启动类添加MapperScan
第七步:连接数据库
# 设置端口为8080
server.port=8080
# 连接数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mp?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
run 成功 lucy 123
自定义用户登录页面(不需要认证也可以访问的页面)
1. 在配置类中实现相关配置
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired // 这个是一个接口,注入的是它的实现类
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 对应的实现类,使用BCryptPasswordEncoder 这个类进行加密的
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//是这个配置 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@Override
protected void configure(HttpSecurity http) throws Exception {
//自定义自己编写的登录页面
http.formLogin()
.loginPage("/login.html") //登录页面的设置
.loginProcessingUrl("/user/login") //登录访问路径,表单提交的地方,比如某个controller,但是逻辑是springSecurity自己实现
.defaultSuccessUrl("/test/index").permitAll() //登录成功之后,跳转的路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证
.anyRequest().authenticated()
.and().csrf().disable(); //关闭csrf防护 , 默认开启的,这个条件是可以忽略的,需要开启
}
}
参考: 用不来,建议查看官网
2. 创建相关页面,controller
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username" />
<br/>
密码:<input type="text" name="password" />
<br/>
<input type="submit" value="login">
</form>
</body>
</html>
这里必须为参数名:username , password 底层代码写的, 必须为post
添加controller
访问:
/test/hello 已经不需要认证,可以直接访问
访问/test/index 会跳转到登录页面,登录成功后,才能访问
登录页面也是直接设置的页面
基于角色或限权进行访问控制
比如:某个页面,必须是管理员才能进行访问
1. hasAuthority 方法
如果当前的主体具有指定的权限,则返回 true,否则返回 false
修改配置类
对应cotroller
设置权限
访问即可。
- 除了不需要登录认证,能直接访问的页面,其他的页面都需要登录后,才能进行访问
- 在登录认证后的,某些网页是需要进行的角色才能进行访问的。
- 访问了没有权限的网页,会显示403。
2. hasAnyAuthority 方法
该用户的角色是 可以访问该网页角色中的一种,就可以访问。
修改配置类
用户角色
访问
登录后,访问/test/find 可以 , /test/findAll 不行 403页面
3. hasRole和hasAnyRole 方法
同理
如果用户具备给定角色就允许访问,否则出现 403。
如果当前主体具有指定的角色,则返回 true。
修改角色
访问
用户角色
登录后,
自定义403页面
1. 修改配置类:
http.exceptionHandling().accessDeniedPage("/unauth");
2. 添加对应控制器或者是页面
@GetMapping("/unauth")
public String accessDenyPage(){
return "unauth";
}
unauth.html
<body>
<h1>对不起,您没有访问权限!</h1>
</body>
访问没有权限的网页就会出现该页面
注解使用
1. @Secured
判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
用户有该限权是可以直接访问对应的controller的
@SpringBootApplication
@MapperScan("com.atguigu.mapper")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SpringSecurityMainApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityMainApplication.class,args);
}
}
@GetMapping("secured")
@Secured("ROLE_role")
public String helloUser(){
return "hello,user";
}
2. @PreAuthorize 和 @PostAuthorize
PreAuthorize 先认证权限,通过后再执行方法 ,
PostAuthorize 先执行方法 , 后认证权限
//测试限权,先验证
@GetMapping("preAuthorize")
@PreAuthorize("hasAnyAuthority('menu:system')")
public String preAuthorize(){
System.out.println("preAuthorize");
return "preAuthorize";
}
//后验证
@GetMapping("postAuthorize")
@PreAuthorize("hasAnyAuthority('menu:system')")
public String postAuthorize(){
System.out.println("postAuthorize");
return "postAuthorize";
}
3. @PostFilter 不常用
@PostFilter :权限验证之后对数据进行过滤 留下用户名是 admin1 的数据
表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
4. @PreFilter 不常用
@PreFilter: 进入控制器之前对数据进行过滤
用户注销 / 退出
基于数据库的记住我(rember me)
1. 创建表
可以自动的创建,不用手动创建的
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP,
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
2. 添加数据库的配置文件
# 设置端口为8080
server.port=8080
# 连接数据库
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mp?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
3. 编写配置类
@Configuration
public class BrowserSecurityConfig {
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
// 赋值数据源
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建表,第一次执行会创建,以后要执行就要删除掉!
jdbcTokenRepository.setCreateTableOnStartup(true); //手动创建设置为 false即可!!!
return jdbcTokenRepository;
}
}
4. 修改安全配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired // 这个是一个接口,注入的是它的实现类
private UserDetailsService userDetailsService;
@Autowired
private PersistentTokenRepository tokenRepository; //操作jdbc的那个对象
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 对应的实现类,使用BCryptPasswordEncoder 这个类进行加密的
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//开启记住我功能
http.rememberMe()
.tokenValiditySeconds(400) //设置为400s
.tokenRepository(tokenRepository)
.userDetailsService(userDetailsService);
//设置403页面
http.exceptionHandling().accessDeniedPage("/unauth.html");
//自定义自己编写的登录页面
http.formLogin()
.loginPage("/login.html") //登录页面的设置
.loginProcessingUrl("/user/login") //登录访问路径,表单提交的地方,比如某个controller,但是逻辑是springSecurity自己实现
.defaultSuccessUrl("/success.html").permitAll() //登录成功之后,跳转的路径,这个好像没有进行跳转
.and().authorizeRequests()
.antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证
.antMatchers("/test/find").hasAuthority("role")
.antMatchers("/test/findAll").hasAuthority("admin")
.anyRequest().authenticated()
.and().csrf().disable(); //关闭csrf防护 , 默认开启的,这个条件是可以忽略的,需要开启
//退出
http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();
}
}
5. 页面添加记住我复选框
login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username" />
<br/>
密码:<input type="text" name="password" />
<br/>
记住我:<input type="checkbox"name="remember-me"title="记住密码"/>
<br/>
<input type="submit" value="login">
</form>
</body>
</html>
run 运行, 点击 记住我这个复选框后,将浏览器关闭后,在进行直接访问,是可以直接访问的。