SpringSecurity学习第二天
访问控制url匹配
在前面讲解了认证中所有常用配置,主要是对 http.formLogin() 进行操作。
而在配置类中 http.authorizeRequests() 主要是对url进行控制,也就是我们所说的授权(访问控制)在前面讲解了认证中所有常用配置,主要是对 http.formLogin() 进行操作。而在配置类中 http.authorizeRequests() 主要是对url进行控制,也就是我们所说的授权(访问控制)。
http.authorizeRequests() 也支持连缀写法,总体公式为: url 匹配规则.权限控制方法 通过上面的公式可以有很多 url 匹配规则和很多权限控制方法。这些内容进行各种组合就形成了 Spring Security中的授权。在所有匹配规则中取所有规则的交集。配置顺序影响了之后授权效果,越是具体的应该放在前面,越 是笼统的应该放到后面。
anyRequest()
在表示匹配所有的请求。一般情况下此方法都会使用设置全部内容都需要进行认证。
antMatcher()
public C antMatchers(String… antPatterns)
参数是不定向参数,每个参数是一个 ant 表达式,用于匹配 URL规则。
规则如下:
? : 匹配一个字符
* :匹配 0 个或多个字符
** :匹配 0 个或多个目录
放行所有静态资源
.antMatchers("/static/**").permitAll()
regexMatchers()
使用正则表达式进行匹配。和 antMatchers() 主要的区别就是参数, antMatchers() 参数是 ant 表达式, regexMatchers() 参数是正则表达式。
无论是 antMatchers() 还是 regexMatchers() 都具有两个参数的方法,其中第一个参数都是HttpMethod,表示请求方式,当设置了HttpMethod后表示只有设定的特定的请求方式才执行对应的权限设置。
.antMatchers(HttpMethod.GET,"/login.html").permitAll()
mvcMatchers()
mvcMatchers()适用于配置了 servletPath 的情况。 servletPath 就是所有的 URL 的统一前缀。在 SpringBoot 整合SpringMVC 的项目中可以在 application.properties 中添加下面内容设置 ServletPath
spring.mvc.servlet.path=/名称
在 Spring Security 的配置类中配置 .servletPath() 是 mvcMatchers()返回值特有的方法,antMatchers()和 regexMatchers()没有这个方法。在 servletPath() 中配置了 servletPath 后,mvcMatchers()直接写 SpringMVC 中@RequestMapping()中设置的路径即可。或者直接在前两个匹配器中加上项目路径
.mvcMatchers("/login.html").servletPath("/xxxx").permitAll()
.antMatchers("/xxxx/login.html").permitAll()
内置访问控制方法
Spring Security 匹配了 URL 后调用了 permitAll() 表示不需要认证,随意访问。在 Spring Security 中提供了多种内置控制。
-
permitAll() : permitAll()表示所匹配的 URL 任何人都允许访问。 -
authenticated() : authenticated()表示所匹配的 URL 都需要被认证才能访问。 -
anonymous() : anonymous()表示可以匿名访问匹配的URL。和permitAll()效果类似,只是设置为 anonymous()的 url 会执行 filter 链中 -
denyAll() : denyAll()表示所匹配的 URL 都不允许被访问。 -
rememberMe() : 被“remember me”的用户允许访问 -
fullyAuthenticated() : 如果用户不是被 remember me 的,才可以访问。
角色权限判断
除了之前讲解的内置权限控制。Spring Security 中还支持很多其他权限控制。这些方法一般都用于用 户已经被认证后,判断用户是否具有特定的要求。
-
hasAuthority(String)判断用户是否具有特定的权限,用户的权限是在自定义登录逻辑中创建 User 对象时指定的。hasAnyAuthority(String …)则匹配多个 在配置类中通过 hasAuthority(“admin”)设置具有 admin 权限时才能访问。例子中admin和normal 就是用户的权限。权限 严格区分大小写。 AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal”)
.antMatchers("/main1.html").hasAuthority("admin")
-
hasRole(String) /hasAnyRole(String …): 如果用户具备给定角色就允许访问。否则出现 403
参数取值来源于自定义登录逻辑UserDetailsService实现类中创建User对象时给User赋予的授权在给用户赋予角色时角色需要以:ROLE_开头,后面添加角色名称。例如:ROLE_abc其中abc是角色名,ROLE_是固定的字符开头使用hasRole()时参数也只写 abc 即可。否则启动报错。
return new User("admin",password, AuthorityUtils.
commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc,/main.html"));}
- hasIpAddress(String) 如果请求是指定的 IP 就运行访问。
.antMatchers("/main1.html").hasIpAddress("127.0.0.1")
自定义403处理方案
spingSeccurity自带的403界面应当被替换,Spring Security 支持自定义权限受限。当用户试图访问无权限资源时会403
新建类实现 AccessDeniedHandler
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter writer = httpServletResponse.getWriter();
writer.write("{\"status\":\"403\",\"msg\":\"权限不足,请联系管理员\"}");
writer.flush();
writer.close();
}
}
修改配置类
配置类中重点添加异常处理器。设置访问受限后交给哪个对象进行处理。 myAccessDeniedHandler 是在配置类中进行自动注入的。
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
http.exceptionHandling()
.accessDeniedHandler(myAccessDeniedHandler);
基于表达式的访问控制
之前学习的登录用户权限判断实际上底层实现都是调用access(表达式),可以通过 access() 实现和之前学习的权限控制完成相同的功能。
.antMatchers("/main1.html").access("hasAnyAuthority('admin,Admin')")
.antMatchers("/main1.html").hasAnyAuthority("admin","Admin")
使用自定义方法
虽然这里面已经包含了很多的表达式(方法)但是在实际项目中很有可能出现需要自己自定义逻辑的情况.判断登录用户是否具有访问当前 URL 权限。
新建接口及实现类
public interface MyService {
boolean hasPermission(HttpServletRequest request, Authentication authentication);
}
@Service
public class MyServiceImpl implements MyService {
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
String uri = request.getRequestURI();
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails){
UserDetails userDetails = (UserDetails) principal;
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
return authorities.contains(new SimpleGrantedAuthority(uri));
}
return false;
}
}
修改配置类
在 access 中通过@bean的id名.方法(参数)的形式进行调用配置类中修改如下:
.anyRequest().access("@myServiceImpl.hasPermission(request,authentication)");
基于注解的访问控制
在 Spring Security 中提供了一些访问控制的注解。这些注解都是默认是都不可用的,需要通过@EnableGlobalMethodSecurity 进行开启后使用。注解可以写到 Service 接口或方法上,也可以写到 Controller或 Controller 的方法上。通常情况下 都是写在控制器方法上的,控制接口URL是否允许被访问。
开启注解(在启动类 , 也可以在配置类等能够扫描的类上添加)
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
-
@Secured @Secured 是专门用于判断是否具有角色的。能写在方法或类上。参数要以 ROLE_开头。比如在控制器方法上添加@Secured 注解。也可以传入多个角色,参数是数组类型 @Secured("ROLE_abc")
@RequestMapping("/toMain")
public String toMain(){
return "redirect:/main.html";
}
-
@PreAuthorize/@PostAuthorize: @PreAuthorize 和@PostAuthorize 都是方法或类级别注解。也需要去配置类中开启注解 @PreAuthorize 表示访问方法或类在执行之前先判断权限,大多情况下都是使用这个注解,注解
的参数和access()方法参数取值相同,都是权限表达式。
@PostAuthorize 表示方法或类执行结束后判断权限,此注解很少被使用到。
@PreAuthorize("hasRole('ROLE_abc')")
@RequestMapping("/toMain")
public String toMain(){
return "redirect:/main.html";
}
拓展:@Secured和@PreAuthorize在Spring Security 3中有什么区别? 如果你只想在用户具有Role1和Role2的情况下执行某些操作,则必须使用@PreAuthorize,而@Secured中数组元素则是或的关系无法完成共有两角色的限定 @PreAuthorize("hasRole('ROLE_role1') and hasRole('ROLE_role2')")
|