参考的优秀的文章
- https://blog.csdn.net/qq_40298902/article/details/106433192
- https://www.cnblogs.com/ymstars/p/10626786.html (流程理解)
- https://juejin.cn/post/6854573217940668430 (快速入门和实战)
理解
-
springsecurity是spring提供的认证授权和攻击防护框架。用来进行安全认证服务。
-
springsecurity充分利用了ioc,aop思想和过滤器。
-
springsecurity的大致流程是通过多个过滤器形成的一条过滤器链来拦截验证请求,通过这条过滤器链才可以访问api。
执行流程:
过滤器链中主要几个过滤器及其作用:
SecurityContextPersistenceFilter:这个Filter是整个拦截过程的入口和出口
UsernamePasswordAuthenticationFilter:用来处理来自表单提交的认证
FilterSecuritylnterceptor:是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问
ExceptionTranslationFilter:捕获来自FilterChain所有的异常并进行处理。但是它只会处理两类异 常:Authentication Exception 和 AccessDeniedException ,其它的异常它会继续抛出。
大概流程:
(1)UsernamePasswordAuthenticationFilter获取表单输入的用户名和密码等请求信息,并封装成Token
(2)AuthenticationManager负责将Token委托给DaoAuthenticationProvider进行认证
(3)DaoAuthenticationProvider通过UserDetailsService来加载要验证的用户
(4)最后先校验用户的账号是否被锁了等信息,再校验用户账号和密码
(5)校验成功则可以访问接口,失败则抛出异常
常用的注解:
@PreAuthorize
-
使用这个注解要先在配置类 SpringSecurityConfig 前加上注解@EnableGlobalMethodSecurity(prePostEnabled=true)
-
这个注解的作用是过滤权限操作的,只有拥有权限才能执行它标注的方法,例如:
# 只有当前用户拥有role1角色才能访问/roleAuth @PreAuthorize("hasRole('role1')") @GetMapping("/roleAuth") public String role() { return "role_lisi"; }
-
此外,这个注解也支持多个条件:
# 访问/test要用户的id小于10且用户名要符合 @PreAuthorize("#id<10 and principal.username.equals(#username)") @GetMapping("/test") public String test(Integer id, String username) { return "注解test"; }
@PostAuthorize
-
同样,使用这个注解要在配置类前加上注解:@EnableGlobalMethodSecurity(prePostEnabled=true)
-
在方法执行后再进行权限验证,适合验证带有返回值的权限,Spring EL 提供 返回对象能够在表达式语言中获取返回的对象returnObject。
@GetMapping("/helloUser") @PostAuthorize(" returnObject!=null && returnObject.username == authentication.name") public User helloUser() { Object pricipal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); User user; if("anonymousUser".equals(pricipal)) { user = null; }else { user = (User) pricipal; } return user; }
@Secured
-
同样,使用这个注解要在配置类前加上注解:@EnableGlobalMethodSecurity(securedEnabled=true)
-
作用和@PreAuthorize类似:
# 拥有normal或admin角色权限的用户可以访问/helloUser,这里匹配的字符串要加前缀ROLE_才行 @GetMapping("/helloUser") @Secured({"ROLE_normal","ROLE_admin"}) public String helloUser() { return "hello,user"; }
使用步骤
- 1. 新建一个springboot项目,引入springweb和springSecurity的包(初始化时选的是war包)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sYGhzCP8-1627379097379)(https://cdn.jsdelivr.net/gh/chenshuai-coder/CDN/img/20210726222643.png)]
创建项目时如果使用war包的方式,则除了主启动类application外,还有一个ServletInitializer类,只在war包项目,即外置servlet容 器项目中存在。
参考文章:https://blog.csdn.net/qq_28289405/article/details/81279742
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cEMjn8Sa-1627379097382)(https://cdn.jsdelivr.net/gh/chenshuai-coder/CDN/img/20210726225638.png)]
package com.mmall;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringsecutityApplication.class);
}
}
- 2. 编写config配置类
package com.mmall;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-07-26
* Time: 15:29
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserService myUserService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.inMemoryAuthentication().withUser("admin").password("123456").roles("admin");
/*使用内存来存储用户,密码,角色信息*/
/*BCryptPasswordEncoder() 系统内置的对密码进行加密的类*/
auth.inMemoryAuthentication().
passwordEncoder(new BCryptPasswordEncoder())
.withUser("root").password(new BCryptPasswordEncoder()
.encode("123456")).roles("admin")
.and()
.withUser("lisi").password(new BCryptPasswordEncoder()
.encode("123456")).roles("role1")
.and()
.withUser("zhangsan").password(new BCryptPasswordEncoder()
.encode("123456")).roles("role2");
/*在数据库中存储用户,密码,角色信息
* 自定义的信息写在MyUserService这个类中*/
auth.userDetailsService(myUserService);
}
@Override
/*定义规则,'/'路径和退出lgout及表单登录不被约束,其他的都要经过验证*/
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.logout().permitAll()
.and()
.formLogin();
/*关闭默认的csrf*/
http.csrf().disable();
}
@Override
/* * "/js/**","/css/**","img/**"这些资源不拦截 */
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/js/**","/css/**","img/**");
}
}
- 3. 在controller包下的代码中使用:
package com.mmall.Controller;
import org.apache.catalina.User;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-07-26
* Time: 15:35
*/
@RestController
@RequestMapping("")
public class main {
@GetMapping("/")
public String index() {
return "hello,springSecurity";
}
@GetMapping("/hello")
public String hello() {
return "hello";
}
/*这个注解是用来过滤权限的,只有拥有ROLE_role1 权限才能访问
* 这个注解需要在配置类前加@EnableGlobalMethodSecurity(prePostEnabled=true)才能使用*/
@PreAuthorize("hasRole('role1')")
@GetMapping("/roleAuth")
public String role() {
return "role_lisi";
}
/*对权限的条件验证 使用注解 @PreAuthorize*/
@PreAuthorize("#id<10 and principal.username.equals(#username) and #user.username.equals(abc)")
@GetMapping("/test")
public String test(Integer id, String username, User user) {
return "注解test";
}
}
- 4. 如果不使用内存改用数据库来存储用户,角色,权限,则创建一个MyUserService类来在里边写。
package com.mmall;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-07-26
* Time: 16:32
*/
@Component
public class MyUserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return null;
}
}