前三章主要讲的是Security对于认证的处理,那么本节,会为大家介绍基于Security默认表达式的权限控制(较为简单,无法满足复杂权限控制及多变的权限规则,后面会介绍基于自定义表达式的权限访问控制,可满足99%的业务场景的需求)。
一、介绍及使用
直接进入主题,基于默认的表达式权限控制,需要在SecurityConfig中配置,代码如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private SecurityProperties securityProperties;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage(securityProperties.getToLoginProperties().getLoginProperties().getLoginPage())//用户未认证时,转跳到认证的页面
.loginProcessingUrl(securityProperties.getToLoginProperties().getLoginProperties().getLoginProcessingUrl())//form中action的地址,也就是处理认证请求的URL
.usernameParameter(securityProperties.getToLoginProperties().getLoginProperties().getUsernameParameter())//form中用户名密码的name名
.passwordParameter(securityProperties.getToLoginProperties().getLoginProperties().getPasswordParameter())
.defaultSuccessUrl(securityProperties.getToLoginProperties().getLoginProperties().getDefaultSuccessUrl())//认证成功后默认转跳的URL
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers(securityProperties.getToLoginProperties().getLoginProperties().getLoginPage(),
securityProperties.getToLoginProperties().getLoginProperties().getLoginProcessingUrl()).permitAll()
.antMatchers(HttpMethod.GET,"/vip").hasRole("admin")//默认权限表达式
.anyRequest().authenticated()
.and()
.csrf().disable()
;
}
}
上面代码的含义是,“/vip”这个URL必须是GET请求,且拥有admin权限的用户才可以访问。接下来我们配置一下UserDetailServer,模拟给用户一个admin权限。
@Component
public class MyUserDetailsServer implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
if("admin".equals(username)){
throw new RuntimeException("admin禁止登录");
}
return new User(username, "$2a$10$ofPkBDUezOJp6Sik63Q/0.QlU8a1itEyzldjSXqfn2nDPqXjN0Ljm", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
AuthorityUtils是Security提供的一个工具类,我使用的这个方法的功能是将一个以“,”分割的字符串转换成UserDetail中第三个参数所需要的Collection<? extends GrantedAuthority>。
全部配置好后我们重启项目,登录。可以看到,我是拥有admin这个权限的,按道理说我有权限访问“/vip”,我们访问一下试试。
奇怪?我明明拥有“admin”权限,为什么访问“/vip”确是403,请求被拒绝呢?
二、默认权限表达式源码解析
第一节中,我们明明可以在Authorites看到当前用户拥有了admin权限,但是却访问被拒绝!这又是为什么呢?下面,我带着大家走一边Security的源码,看一看到底是哪里出现了问题。
我们依旧在UserdetailsServer中打上断点。
重启项目,登陆后,访问“/vip”查看断点堆栈信息。我们只看最后一个过滤器,这里我们说过,他会读取我们的SecurityConfig的配置信息,做匹配。我们可以看到虽然我们配置的“/vip”需要的权限是admin,但是框架却自动为我们加上了“ROLE_”的前缀,所以,这就是我们为什么匹配不到,无权限访问的原因。
我们稍作修改,将UserDetailsServer中的权限加上这个前缀。
我们重启项目,再次访问,可以看到已经有权限了,404是由于我没有写这个页面。
三、自定义异常页面
在前面的介绍中,无论是403还是404,我们使用的都是提供的默认页面实现,而在我们的实际业务中需要我们使用自己的异常页面,那么怎么配置呢?方法有很多种,比如404页面,我们可以写一个不完全匹配的@RequestMapping 这样由于不完全匹配的优先级是小于完全匹配的优先级的,所以当匹配不到映射的时候就会匹配我们的404了。
但是我既然单独拿出来写一章,其实完全为了凑字数(一不小心说了实话),那肯定就是有更好的方法的,其实在Spring Boot中,只需要在默认静态资源目录下创建一个与状态码同名的页面,就会在出现异常的时候进入到你的页面中。至于Spring boot的默认资源目录是什么,怎么改,请看我的Spring boot系列博客。
我们访问试一下: