用户授权
授权的方式包括 web授权和方法授权,web授权是通过 url拦截进行授权,方法授权是通过 方法拦截进行授权。他 们都会调用accessDecisionManager进行授权决策,若为web授权则拦截器为
FilterSecurityInterceptor;若为方 法授权则拦截器为MethodSecurityInterceptor。如果同时通过web授权和方法授权则先执行web授权,再执行方 法授权,最后决策通过,则允许访问资源,否则将禁止访问。
web授权
Spring Security可以通过 http.authorizeRequests() 对web请求进行授权保护 ,Spring Security使用标 准Filter建立了对web请求的拦截,最终实现对资源的授权访问。
http.authorizeRequests()
//设置哪些路径可以直接访问,不需要认证
.antMatchers("/user/login", "/login.html").permitAll()
.anyRequest().authenticated(); //需要认证才能访问
访问控制的url匹配
在配置类中http.authorizeRequests() 主要是对url进行控制。配置顺序会影响之后授权的效果,越是具体的应该放在前面,越是笼统的应该放到后面。
1、anyRequest()
表示匹配所有的请求。一般情况下此方法都会使用,设置全部内容都需要进行认证,会放在最后。
.anyRequest().authenticated()
2、antMatchers()
方法定义如下:
public C antMatchers(String… antPatterns)
参数是不定向参数,每个参数是一个 ant 表达式,用于匹配 URL规则。
ANT通配符有三种:
通配符 | 说明 | |
---|---|---|
? | 匹配任何单字符 | |
* | 匹配0或者任意数量的字符 | |
** | 匹配0或者更多的目录 |
在实际项目中经常需要放行所有静态资源:
// 放行 js和css 目录下所有的文件
.antMatchers("/js/**", "/css/**").permitAll()
// 只要是.js 文件都放行
.antMatchers("/**/*.js").permitAll()
3、regexMatchers()
使用正则表达式进行匹配。
//所有以.js 结尾的文件都被放行
.regexMatchers( ".+[.]js").permitAll()
无论是 antMatchers() 还是 regexMatchers() 都具有两个参数的方法,其中第一个参数都是 HttpMethod ,表示请求方式,当设置了 HttpMethod 后表示只有设定的特定的请求方式才执行对应的
权限设置。
.antMatchers(HttpMethod.POST, "/admin/demo").permitAll()
.regexMatchers(HttpMethod.GET, ".+[.]jpg").permitAll()
4、mvcMatchers()
适用于配置了 servletPath 的情况。 servletPath 就是所有的 URL 的统一前缀。在 SpringBoot 整合 SpringMVC 的项目中可以在application.properties 中添加下面内容设置 ServletPath。
spring.mvc.servlet.path=/web
在 Spring Security 的配置类中配置 .servletPath() 是 mvcMatchers()返回值特有的方法, antMatchers()和 regexMatchers()没有这个方法。在 servletPath() 中配置了 servletPath 后,
mvcMatchers()直接写 SpringMVC 中@RequestMapping()中设置的路径即可。
.mvcMatchers("/admin/demo").servletPath("/web").permitAll()
如果不习惯使用 mvcMatchers()也可以使用 antMatchers(),下面代码和上面代码是等效的:
.antMatchers("/web/admin/demo").permitAll()
RequestMatcher接口
RequestMatcher 是 Spring Security Web 的一个概念模型接口,用于抽象建模对 HttpServletRequest 请求的匹配器这一概念。 Spring Security 内置提供了一些 RequestMatcher 实现类:
内置的访问控制
-
【常用】 #permitAll() 方法,所有用户可访问。
-
【常用】 #denyAll() 方法,所有用户不可访问。
-
【常用】 #authenticated() 方法,登录用户可访问。
-
#anonymous() 方法,无需登录,即匿名用户可访问。
-
#rememberMe() 方法,通过 remember me登录的用户可访问。
-
#fullyAuthenticated() 方法,非 remember me 登录的用户可访问。
-
#hasIpAddress(String ipaddressExpression) 方法,来自指定 IP 表达式的用户可访问。
-
【常用】 #hasRole(String role) 方法, 拥有指定角色的用户可访问,角色将被增加 “ROLE_” 前缀。
-
【常用】 #hasAnyRole(String… roles) 方法,拥有指定任一角色的用户可访问。
-
【常用】 #hasAuthority(String… authorities) 方法,拥有指定任一权限( authority )的用 户可访问。
-
【最牛】 #access(String attribute) 方法,当 Spring EL 表达式的执行结果为 true 时,可以 访问。
基于权限的访问控制
除了之前讲解的内置权限控制。Spring Security 中还支持很多其他权限控制。这些方法一般都用于用户已经被认证后,判断用户是否具有特定的要求。
1、hasAuthority(String)
判断用户是否具有特定的权限,用户的权限是在自定义登录逻辑中创建 User 对象时指定的。权限名称大小写敏感
return new User("zhengyao",pw, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,user"));
在配置类中通过 hasAuthority(“admin”)设置具有 admin 权限时才能访问。
.antMatchers("/admin/demo").hasAuthority(“admin”)
2、hasAnyAuthority(String …)
如果用户具备给定权限中某一个,就允许访问。
.antMatchers("/admin/demo").hasAnyAuthority(“admin”,“System”)
基于角色的访问控制
1、hasRole(String)
如果用户具备给定角色就允许访问,否则出现 403。参数取值来源于自定义登录逻辑 UserDetailsService 实现类中创建 User 对象时给 User 赋予的授权。 在给用户赋予角色时角色需要以:
ROLE_ 开头 ,后面添加角色名称。例如:ROLE_admin 其中 admin是角 色名, ROLE_ 是固定的字符开头。
return new User(“zhengyao”,pw, AuthorityUtils.commaSeparatedStringToAuthorityList(“ROLE_ admin,user”));
使用 hasRole()时参数也只写admin 即可,否则启动报错。
.antMatchers("/admin/demo").hasRole(“admin”)
2、hasAnyRole(String …)
如果用户具备给定角色的任意一个,就允许被访问
3、hasIpAddress(String)
如果请求是指定的 IP 就运行访问。 可以通过 request.getRemoteAddr() 获取 ip 地址。需要注意的是在 本机进行测试时 localhost 和 127.0.0.1 输出的 ip地址是不一样的。
// localhost --> getRemoteAddr: 0:0:0:0:0:0:0:1
.antMatchers("/admin/demo").hasIpAddress(“127.0.0.1”)
基于表达式的访问控制
1、access(表达式)
可以通过 access() 实现和之前学习的权限控制完成相同的功能。
.antMatchers("/user/login","/login.html").access(“permitAll”)
.antMatchers("/admin/demo").access(“hasAuthority(‘System’)”)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ewqrdIIZ-1611488285517)(C:\Users\郑瑶\Desktop\T\imag\鉴权\2\1610006483(1)].png)
2、自定义方法
判断登录用户是否具有访问当前 URL 的权限。
public interface MySecurityExpressionOperations {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
@Component
public class MySecurityExpression implements MySecurityExpressionOperations{
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
// 获取主体
Object obj = authentication.getPrincipal();
if (obj instanceof UserDetails){
UserDetails userDetails = (UserDetails) obj;
//
String name = request.getParameter("name");
//获取权限
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
//判断name值是否在权限中
return authorities.contains(new SimpleGrantedAuthority(name));
}
return false;
}
}
在 access 中通过bean的beanName.方法(参数)的形式进行调用:
.anyRequest().access("@mySecurityExpression.hasPermission(request,authentication )")
自定义403处理方案
使用 Spring Security 时经常会看见 403(无权限)。Spring Security 支持自定义权限受限处理,需要 实现 AccessDeniedHandler接口
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write("{\"status\":\"error\",\"msg\":\"权限不足,请联系管理员!\"}");
out.flush();
out.close();
}
}
在配置类中设置访问受限后交个MyAccessDeniedHandler处理
//配置403(无权限)
http.exceptionHandling()
.accessDeniedHandler(new MyAccessDeniedHandler());
方法授权
基于注解的访问控制
Spring Security在方法的权限控制上支持三种类型的注解,JSR-250注解、@Secured注解和支持表达式 的注解。这三种注解默认都是没有启用的,需要通过@EnableGlobalMethodSecurity来进行启用。 这些注解可以写到 Service 接口或方法上,也可以写到 Controller或 Controller 的方法上。通常情况下都是写在控制器方法上的,控制接口URL是否允许被访问。
JSR-250注解
开启注解 在启动类或者在配置类上添加 @EnableGlobalMethodSecurity(jsr250Enabled = true)
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
@RolesAllowed({"ROLE_USER", "ROLE_ADMIN"})
//@PermitAll
@GetMapping("/demo")
public String demo() {
return "spring security demo";
}
1、@RolesAllowed
表示访问对应方法时所应该具有的角色。其可以标注在类上,也可以标注在方法上,当标注在类上时表 示其中所有方法的执行都需要对应的角色,当标注在方法上表示执行该方法时所需要的角色,当方法和 类上都使用了@RolesAllowed进行标注,则方法上的@RolesAllowed将覆盖类上的@RolesAllowed,即 方法上@RolesAllowed将对当前方法起作用。@RolesAllowed的值是由角色名称组成的数组。
2、@PermitAll
表示允许所有的角色进行访问,也就是说不进行权限控制。@PermitAll可以标注在方法上也可以标注在 类上,当标注在方法上时则只对对应方法不进行权限控制,而标注在类上时表示对类里面所有的方法都不进行权限控制。
-
当@PermitAll标注在类上,而@RolesAllowed标注在方法上时则按照 @RolesAllowed将覆盖@PermitAll,即需要@RolesAllowed对应的角色才能访问。
-
当@RolesAllowed标注在类上,而@PermitAll标注在方法上时则对应的方法也是不进行权限控制 的。
-
当在类和方法上同时使用了@PermitAll和@RolesAllowed时先定义的将发生作用(这个没多大的 实际意义,实际应用中不会有这样的定义)。
3、@DenyAll
是和PermitAll相反的,表示无论什么角色都不能访问。@DenyAll只能定义在方法上。你可能会有疑问使用@DenyAll标注的方法无论拥有什么权限都不能访问,那还定义它干啥呢?使用@DenyAll定义的方 法只是在我们的权限控制中不能访问,脱离了权限控制还是可以访问的。
@Secured注解
@Secured是由Spring Security定义的用来支持方法权限控制的注解。它的使用也是需要启用对应的支 持才会生效的。@Secured 是专门用于判断是否具有角色的,能写在方法或类上。参数要以 ROLE_开头。
开启注解 在启动类或者在配置类上添加 @EnableGlobalMethodSecurity(securedEnabled = true)
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter { }
@Secured("ROLE_ADMIN")
@GetMapping("/demo")
public String demo() {
return "spring security demo";
}
支持表达式的注解
Spring Security中定义了四个支持使用表达式的注解,分别是@PreAuthorize、@PostAuthorize、 @PreFilter和@PostFilter。其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {}
1、使用@PreAuthorize和@PostAuthorize进行访问控制
@PreAuthorize可以用来控制一个方法是否能够被调用,执行之前先判断权限,大多情况下都是使用这个注解。
//@PreAuthorize("hasRole('ROLE_ADMIN')")
// @PreAuthorize("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
// 限制只能查询Id小于10的用户
@PreAuthorize("#id<10")
@RequestMapping("/findById")
public User findById(long id) {
User user = new User();
user.setId(id);
return user;
}
// 限制只能查询自己的信息
@PreAuthorize("principal.username.equals(#username)")
@RequestMapping("/findByName")
public User findByName(String username) {
User user = new User();
user.setUsername(username);
return user;
}
//限制只能新增用户名称为abc的用户
@PreAuthorize("#user.username.equals('abc')")
@RequestMapping("/add")
public User add(User user) {
return user;
}
@PostAuthorize可以在方法调用完之后进行权限检查
// 在方法find()调用完成后进行权限检查,如果返回值的id是偶数则表示校验通过,否则表示校验失败,将 抛出AccessDeniedException
@PostAuthorize("returnObject.id%2==0")
public User find(int id) {
User user = new User();
user.setId((long) id);
return user;
}
2、使用@PreFilter和@PostFilter进行过滤
使用@PreFilter和@PostFilter可以对集合类型的参数或返回值进行过滤。使用@PreFilter和@PostFilter时,Spring Security将移除使对应表达式的结果为false的元素。
@PostFilter("filterObject.id%2==0")
public List<User> findAll() {
List<User> userList = new ArrayList<User>();
User user;
for (int i = 0; i < 10; i++) {
user = new User();
user.setId(i);
userList.add(user);
} return userList;
}
@PreFilter(filterTarget = "ids", value = "filterObject%2==0")
public void delete(List<Integer> ids, List<String> usernames) {
}
PostFilter("filterObject.id%2==0")
public List<User> findAll() {
List<User> userList = new ArrayList<User>();
User user;
for (int i = 0; i < 10; i++) {
user = new User();
user.setId(i);
userList.add(user);
} return userList;
}
@PreFilter(filterTarget = "ids", value = "filterObject%2==0")
public void delete(List<Integer> ids, List<String> usernames) {
}