Spring Security
框架简介
概要
Spring Security基于Spring框架,提供了一套Web应用安全性的完整解决方案,用户认证和用户授权是Spring Security重要核心功能
用户认证:系统认为用户能否登录
用户授权:系统判断用户是否有权限去做某些事情
与Shiro对比
SPring Security特点:
- 和Spring无缝整合
- 全面的权限控制
- 专门为Web开发而设计
- 旧版本不能脱离Web环境使用
- 新版本对整个框架进行了分成抽取,分成了核心模块和Web模块。单独引入核心模块就可以脱离Web环境
- 重量级
Shiro
-
轻量级。Shiro主张的理念是吧复杂的事情简单化,针对对性能有更高要求的互联网应用有更好的表现
-
通用性
-
好处:不局限于Web环境,可以脱离Web环境使用
-
缺陷:在Web环境下一些特定的需求需要手动编写代码定制
Security基本原理
SpringSecurity本质上是一个过滤器链,有很多过滤器
代码底层流程,重点看三个过滤器
FilterSecurityInterceptor:是一个方法及的过v零七,基本位于过滤链的最低端
ExceptionTranskationFilter:是异常过滤器,用来处理在认证授权过程中抛出的异常
UserbanePasswirdAuthenticationFilter:对login的POST请求做拦截,校验表单中用户名和密码
入门案列
创建正常的Springboot工程
引入相关依赖
<!--SpringSecSecurity-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
编写controller进行测试
@Controller
public class Contriller {
@RequestMapping("/hello")
@ResponseBody()
public String hello(){
return "hello";
}
}
运行成功后,会出现本次登录的密码
然后访问方法,会出现登录界面,输入密码,即可成功访问
两个重要的接口
UserDetallService接口:查询数据库用户名和密码过程
创建类继承UsernamePasswordAuthenticationFilter,重写方法
创建类实现UserDetailService,编写拆线呢数据过程,返回User对象,这个user对象是安全框架提供对象
PasswordEncoder接口:数据加密接口,用于返回User对象里面的密码加密
Web权限方案
认证
1、设置登录的用户名和密码
第一种方式:通过配置文件
在application.properties中填写配置文件
第二种方式,通过配置类
创建config包,在包中创建SecurityConfig类,用来配置信息
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 对密码进行加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("123456");
// 设置账号密码
auth.inMemoryAuthentication().withUser("zhangsan").password(password).roles("admin");
}
// 实现加密接口
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
第三种方式,自定义编写实现类
第一步 创建配置类,设置使用那个userDetailsService实现类
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
// 获取存储密码的UserDetailsService对象
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 从userDetailsService中获取密码,并加密
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
// 实现加密接口
@Bean
PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
}
第二步编写实现类,返回User对象,User对象有用户名密码和操作权限
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 获取用户的角色(权限)
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 返回用户信息,用户名,密码,角色
return new User("lisi",new BCryptPasswordEncoder().encode("123456"),auths);
}
}
查询数据库完成用户认证
第一步 引入相关依赖
<!--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>
第二步 创建数据库和数据表
第三步 创建与数据表对应的实体类
@Data
public class User {
private Integer id;
private String username;
private String password;
}
第四步 整合mapper,创建接口,继承MybatisPlus的mapper的接口
@Repository
public interface UserMapper extends BaseMapper<User> {
}
第五步 在UserDetailsService调用mapper里面的方法查询数据库进行用户认证
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库中查询用户名对应的密码
// 设置条件查询
QueryWrapper<User> wrapper=new QueryWrapper<>();
wrapper.eq("username",username);
User user=userMapper.selectOne(wrapper);
if(user==null){
throw new UsernameNotFoundException("用户名不存在");
}
// 获取用户的角色(权限)
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 返回用户信息,用户名,密码,角色
return new org.springframework.security.core.userdetails.User(username,new BCryptPasswordEncoder().encode(user.getPassword()),auths);
}
}
第六步 在启动类上添加扫描注解 MapperScan
@SpringBootApplication
@MapperScan("com.springsecuritty.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
第七步 配置数据库信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/userinfo?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=shou00736
自定义用户登录界面
1、在配置类中实现相关配置
// 重写自动跳转到登录的方法
@Autowired
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定义自己编写的登录页面
.loginPage("/lofin.html") //设置需要跳转的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.defaultSuccessUrl("/test/index").permitAll() //设置登录成功后访问的路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","user/login").permitAll()//设置可以直接访问的,不需要认证的地址
.anyRequest().authenticated().and().csrf().disable();//关闭csrf的保护
}
2、创建相关前端登录页面和跳转的controller方法
<form action="/user/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
<input type="submit" value="登录">
</form>
@RequestMapping("/test/index")
@ResponseBody
public String index(){return "index";}
授权
基于角色或权限进行访问控制
第一个方法:hasAuthority方法(针对用户只拥有一个权限)
如果当前的主题具有指定的权限,则返回true否则返回false
// 重写自动跳转到登录的方法
@Autowired
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定义自己编写的登录页面
.loginPage("/login.html") //设置需要跳转的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.defaultSuccessUrl("/test/index").permitAll() //设置登录成功后访问的路径
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()//设置可以直接访问的,不需要认证的地址
//当前登录用户,只有具有admins权限才可以访问这个路径
.antMatchers("/test/index").hasAuthority("admins")
.anyRequest().authenticated().and().csrf().disable();//关闭csrf的保护
}
第二个方法:hasAnyAuthority方法(针对用户拥有多个权限)
如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的化,返回true
// 重写自动跳转到登录的方法
@Autowired
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定义自己编写的登录页面
.loginPage("/login.html") //设置需要跳转的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.defaultSuccessUrl("/test/index").permitAll() //设置登录成功后访问的路径
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login").permitAll()//设置可以直接访问的,不需要认证的地址
//当前登录用户,只有具有admins权限才可以访问这个路径
.antMatchers("/test/index").hasAnyAuthority("admins,rule")
.anyRequest().authenticated().and().csrf().disable();//关闭csrf的保护
}
}
第三种方法:hasRole方法
如果当前主体具有指定的角色,则返回true
第四种方法:hasAnyRole方法
表示用户具有任何一个条件都可以访问
注意:使用后面两种方法,在源码中会在权限名称前+ROLE_
自定义403没有权限访问页面
在配置类中进行配置
// 重写自动跳转到登录的方法
@Autowired
protected void configure(HttpSecurity http) throws Exception {
// 配置没有权限访问跳转自定义界面
http.exceptionHandling().accessDeniedPage("/unauth.html");
}