授权呢就是分配访问权限,可使用的方法,我们可通过点击进入传的参数对象进行查看。
antMatcher表示的是匹配放行,而antMatchers则是放行匹配到的多个资源的意思,是通过传入可变参实现的。
通常我们都需要放行一些通过的路径,如登录错误等,还有一个就是静态的资源。
值得注意的是b.jpg有可能被拦截,最后重启项目以及刷新几次maven,而访问c.png则会被拦截,跳转到登录页面,登录后就可以查看到c.png了
使用正则表达式放行
访问jpg会被拦截,而c.png会直接放行。
使用正则匹配http请求:
使用get请求被拦截了,我们使用postman发送post请求试试:
ok没问题
使用postman发post请求访问http://localhost:8686/mvc/hello
重载其他方法
.mvcMatchers(HttpMethod.POST,"/hello").permitAll()
当然以上方法都可以使用antMatchers完成请求:
加个hello world试试
其实内置访问方法我们一直都有使用:permitAll方法和authenticated方法
,看看源码吧:
表达式网址授权配置器:ExpressionUrlAuthorizationConfigurer
可以看到,里面有可以看到有6种
属性,每种属性都对应一个方法,permitAll是允许所有,denyAll是拒绝所有,anonymous是允许匿名的,authenticated是需要认证,fullyAuthenticated是需要完整的认证,rememberMe是记住我
,比如7天免登录这种。这6个方法无法单独使用,需要配合antMatchers
等方法一起使用。
使用方法permitAll就是案例就不累述了,自己尝试即可。
除了内置权限控制。Spring Security中还支持很多其他权限控制。这些方法一般都用于用户已经被认证后,判断用户是否具有特定的要求
。例如我们使用视频网站一些看有vip权限
才能看的视频,这时候就需要根据角色权限
判断了。当然还有更细粒度的直接就是权限对比。
接下来就分别演示使用角色以及权限来完成登录后访问特定资源
首先把此前使用mvc前缀的配置注释掉:
在配置里,我们拥有这个定义的admin和calmtho,所以我们现在登录上的账户是满足在授权匹配要求的的地方代码要求的。
然后我们修改一下前端界面加个子界面
ok现在就登录验证一下,重启应用直接访问localhost:8686/main.html
被拦截,回到登录页
现在把授权要求需要有的权限改了,改成aaa试试,当然为了避免一些其他因素的干扰,我们把页面进行调整,把error.html移到静态页面下,认证失败的自定义页面也先关闭
登录后访问的页面响应为一个异常页面,类型为禁止访问,状态码为403
测试下一个api调用多个权限中有一个即可的方法:
.antMatchers("/main1.html").hasAnyAuthority("aaa","admin")
看一下源码
匹配的时候会拼写一个以ROLE_开头的字符串,所以说明我们UserDetails里那的权限也需要,否则将不匹配。
同样的成功,使用admin角色试试
我们把角色要求变一下,变vip才可以。
不匹配所以不行值得注意的是匹配规则是区分大小写
的,所以需要注意一下,我们可以尝试变Admin要求是不行的,同时测试hasAnyRoles
我们用户有的还是ROLE_admin权限,反正此前干扰结果,我们多刷新几次maven以及重启项目:
403没毛病。
这时候我们再加上admin也可以看看!
在权限与角色的粒度中,角色的权限较粗,而权限方面的粒度则会细一点,我们可以把权限和资源关联起来,例如挂载在菜单上,作为菜单按钮,然后菜单按钮附上权限,比如有这个权限的即可,然后让角色和菜单管理,什么角色配上什么菜单,当突然加了一个角色,我们只需要给这个角色配置相应的菜单即按钮权限,然后用户再管理这个角色即可,进一步解耦。这样直接和用户的耦合就会变小,更易维护。
测试,我们使用localhost和127.0.0.1分别测试,确实有不同的效果
我们把权限匹配请求要求的匹配要求换一下,换成一个没有的角色,登陆后访问main1.html
可见我们此前调用的方法都是走的access()表达,那我们完全可以直接调用access()方法实现和此前一样的效果。
以下为常见的内置表达式:
表达式 | 描述 |
---|---|
hasRole([role]) | 如果当前主体具有指定角色,则返回true。默认情况下,如果提供的角色不以"ROLE_"开头,则会添加该角色。这可以通过修改DefaultWebSecurityExpressionHandler上的defaultRolePrefix来进行自定义。 |
hasAnyRole([role1,role2]) | 如果当前主体具有任何提供的角色(以逗号分隔的字符串列表给出),则会返回true。默认情况下,如果提供的角色不以"ROLE_"开头,则会添加该角色。这可以通过修改DefaultWebSecurityExpressionHandler上的defaultRolePrefix来进行自定义。 |
hasAuthority([authority]) | 如果当前主体具有指定的权限,则返回true |
hasAnyAuthority([authority1,authority2]) | 如果当前主体具有任何提供的权限(以逗号分隔的字符串列表给出),则返回true |
principal | 允许直接访问代表当前用户的主体对象 |
authentication | 允许直接访问从SecurityContext获取的当前Authentication对象 |
permitAll | 始终评估为true |
denyAll | 始终评估为false |
isAnonymous() | 如果当前主体是匿名用户,则返回true |
isRememberMe() | 如果当前主体是remember-me用户,则返回true |
isAuthenticated() | 如果不是匿名用户,则返回true |
isFullyAuthenticated() | 如果不是匿名用户或记住我用户,则返回true |
hasPermission(Object target,Object permission) | 如果用户有权访问给定权限的提供目标,则返回true。例如,hasPermission(domainObject,‘read’) |
hasPermission(Object targetId,String targetType,Object permission) | 如果用户有权访问给定权限的提供目标,则返回true。例如,hasPermission(1,‘com.example.domain.Message’,‘read’) |
试一试:
试试角色判断:
main1.html被拦截了,需要登录认证。
在实际项目中很有可能出现需要自己自定义逻辑的情况。比如判断登录用户是否具有访问当前URL权限。
代码如下:
首先自定义接口:
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
public interface MyService {
/**判断是否有权限*/
boolean hasPermission(HttpServletRequest request,
Authentication authentication);
}
实现类
@Component
public class MyServiceImpl implements MyService {
@Override
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
// 获取当前主体
Object obj = authentication.getPrincipal();
// 如果属于UserDetails
if (obj instanceof UserDetails) {
// 下转型
UserDetails userDetails = (UserDetails) obj;
// 获取主体的所有权限
Collection<? extends GrantedAuthority> authorities = userDetails
.getAuthorities();
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(
request.getRequestURI());
boolean res = authorities.contains(simpleGrantedAuthority);
return res;
}
return false;
}
}
首先说明我们没有直接放行/main这个路径,只是赋予了user对象的权限里有带有url请求。意思是这个登录后有权访问main页面.没登陆会被拦截。认证后即可成功跳转,main页面,但是/main1.html是无权访问的,会响应无权限。
接下来就是使用postman测试:
即先登录:
成功访问到main页面
点解访问main1.html
测试此前编写的post请求的hello方法也一样,说明成功了。
到此我们已经把写在config方法里的可用授权这方面的api都玩了一遍。写的仓促,里面有些多余的东西,例如上面的图片一开始阅读源码有点误会,在方法里new了一个集合,后面忘记删了,还有一个就是子页面的标题忘记改了,还是叫主页面。在这里说明一下,避免产生疑虑