首页 > 学院 > 开发设计 > 正文

springBoot+springSecurity 动态管理Restful风格权限(三)

2019-11-08 19:49:15
字体:
来源:转载
供稿:网友

上一篇博客 sPRingBoot+springSecurity 数据库动态管理用户、角色、权限(二) 只是实现了用户、角色、权限的动态管理,但是其权限管理是有缺陷的,他不支持restful风格的接口权限管理,因为他无法区分客户端的请求方式。

本片博客是为了弥补此缺陷的,本篇博客将在 springBoot+springSecurity 数据库动态管理用户、角色、权限(二) 的基础上进行修改使其支持 restful 风格的接口的权限管理。

本文目录: 1. 分析工作量 2. 修改代码 3. 准备数据 4. 测试


一、分析

首先分析一下工作量吧,因为要支持 restful 风格的接口,那么我们在判断用户是不是有权限访问的时候不仅要判断 url 还要判断 请求方式。 所以我门需要修改数据库表,因为我门的权限表还没有method 字段。

由于要判断 url 和 method 所以要在CustomUserService 类的 loadUserByUsername 方法中要添加 权限的 url 和 method 。但是SimpleGrantedAuthority 只支持传入一个参数。 所以我门考虑要再写一个类 实现 GrantedAuthority 接口,并在构造函数中传入两个参数。嘻嘻。

由于我们不仅要判断url 还要 判断请求方法,所以当然要修改 MyaccessDecisionManager 的decide 方法的内容了。因为:decide 方法是判定是否拥有权限的决策方法 ,三个参数的含义分别为: //authentication 是释CustomUserService中循环添加到 GrantedAuthority 对象中的权限信息集合. //object 包含客户端发起的请求的requset信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); //configAttributes 为MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。当然在 修改一下 MyInvocationSecurityMetadataSourceService 的getAttributes 方法。//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。 //因为我不想每一次来了请求,都先要匹配一下权限表中的信息是不是包含此url,我准备直接拦截,不管请求的url 是什么都直接拦截,然后在MyAccessDecisionManager的decide 方法中做 拦截还是放行的决策。

5.关闭csrf 6.添加restful 风格的接口

好了分析完了,接下来就是编码了。


二、 修改

1. 修改permission表

添加method 字段,当然Permission 的java bean 中 也要添加此属性和其get set方法。

这里写图片描述

//请求方法 private String method; public String getMethod() { return method; } public void setMethod(String method) { this.method = method; }

2. 实现 GrantedAuthority 接口

新建java类MyGrantedAuthority 实现 GrantedAuthority 接口

package com.us.example.service;import org.springframework.security.core.GrantedAuthority;/** * Created by yangyibo on 17/2/15. */public class MyGrantedAuthority implements GrantedAuthority { private String url; private String method; public String getPermissionUrl() { return url; } public void setPermissionUrl(String permissionUrl) { this.url = permissionUrl; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public MyGrantedAuthority(String url, String method) { this.url = url; this.method = method; } @Override public String getAuthority() { return this.url + ";" + this.method; }}

在CustomUserService 类中使用MyGrantedAuthority

public UserDetails loadUserByUsername(String username) { SysUser user = userDao.findByUserName(username); if (user != null) { List<Permission> permissions = permissionDao.findByAdminUserId(user.getId()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); for (Permission permission : permissions) { if (permission != null && permission.getName() != null) { GrantedAuthority grantedAuthority = new MyGrantedAuthority(permission.getUrl(), permission.getMethod()); grantedAuthorities.add(grantedAuthority); } } return new User(user.getUsername(), user.getPassWord(), grantedAuthorities); } else { throw new UsernameNotFoundException("admin: " + username + " do not exist!"); } }

3.修改 MyAccessDecisionManager 的decide 方法

package com.us.example.service;import org.springframework.security.access.AccessDecisionManager;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.authentication.InsufficientAuthenticationException;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.web.FilterInvocation;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;import java.util.Collection;import java.util.Iterator;/** * Created by yangyibo on 17/1/19. */@Servicepublic class MyAccessDecisionManager implements AccessDecisionManager { //decide 方法是判定是否拥有权限的决策方法 @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); String url, method; AntPathRequestMatcher matcher; for (GrantedAuthority ga : authentication.getAuthorities()) { if (ga instanceof MyGrantedAuthority) { MyGrantedAuthority urlGrantedAuthority = (MyGrantedAuthority) ga; url = urlGrantedAuthority.getPermissionUrl(); method = urlGrantedAuthority.getMethod(); matcher = new AntPathRequestMatcher(url); if (matcher.matches(request)) { //当权限表权限的method为ALL时表示拥有此路径的所有请求方式权利。 if (method.equals(request.getMethod()) || "ALL".equals(method)) { return; } } } else if (ga.getAuthority().equals("ROLE_ANONYMOUS")) {//未登录只允许访问 login 页面 matcher = new AntPathRequestMatcher("/login"); if (matcher.matches(request)) { return; } } } throw new AccessDeniedException("no right"); } @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public boolean supports(Class<?> clazz) { return true; }}

4. 修改MyInvocationSecurityMetadataSourceService 的getAttributes 方法

package com.us.example.service;import com.us.example.dao.PermissionDao;import com.us.example.domain.Permission;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.access.ConfigAttribute;import org.springframework.security.access.SecurityConfig;import org.springframework.security.web.FilterInvocation;import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletRequest;import java.util.*;/** * Created by yangyibo on 17/1/19. */@Servicepublic class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { //此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。 //因为我不想每一次来了请求,都先要匹配一下权限表中的信息是不是包含此url, // 我准备直接拦截,不管请求的url 是什么都直接拦截,然后在MyAccessDecisionManager的decide 方法中做拦截还是放行的决策。 //所以此方法的返回值不能返回 null 此处我就随便返回一下。 @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { Collection<ConfigAttribute> co=new ArrayList<>(); co.add(new SecurityConfig("null")); return co; } @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } @Override public boolean supports(Class<?> clazz) { return true; }}

5.关闭csrf

关于 什么是csrf 请看我的这篇博客

spring security CSRF 问题

修改 WebSecurityConfig 的configure(HttpSecurity http) 方法 ,添加 .csrf().disable();

@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() //任何请求,登录后可以访问 .and() .formLogin() .loginPage("/login") .failureUrl("/login?error") .permitAll() //登录页面用户任意访问 .and() .logout().permitAll(); //注销行为任意访问 http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class) .csrf().disable(); }

6. 修改HomeController

由于我们是要测试restful 风格的权限,所以我门要有restful 的接口

package com.us.example.controller;import com.us.example.domain.Msg;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;/** * Created by yangyibo on 17/1/18. */@Controllerpublic class HomeController { @RequestMapping("/") public String index(Model model){ Msg msg = new Msg("测试标题","测试内容","欢迎来到HOME页面,您拥有 ROLE_HOME 权限"); model.addAttribute("msg", msg); return "home"; } @RequestMapping("/admin") @ResponseBody public String hello(){ return "hello admin"; } @RequestMapping("/login") public String login(){ return "login"; } @RequestMapping(value = "/user", method = RequestMethod.GET) @ResponseBody public String getList(){ return "hello getList"; } @RequestMapping(value = "/user", method = RequestMethod.POST) @ResponseBody public String save(){ return "hello save"; } @RequestMapping(value = "/user", method = RequestMethod.PUT) @ResponseBody public String update(){ return "hello update"; }}

好了编码部分完成了

三、准备数据

在数据库中添加测试数据,主要是权限表和权限角色中间表。

结果(角色1 可以访问 /user 下的所有接口, 角色2 只可以访问 /user 下的GET请求)

权限表: 这里写图片描述

权限角色中间表:(此处角色1拥有 权限 6 ,权限6的方法为 ALL 也就是角色6 可以访问所有路径为/user 的接口) 这里写图片描述


四、测试

启动项目,然后在postman 中测试, 1. 登录admin 后访问 user 的所有权限,都可以正常访问。 这里写图片描述

这里写图片描述

这里写图片描述

put 方法访问成功 。

登录abel 后访问 user 的所有权限,只有GET 权限可以访问。

这里写图片描述

这里写图片描述

这里写图片描述

put 方法访问失败。


半夜码字。。。 如果本文对您有帮助请给个好评,谢谢。

本文源码:https://github.com/527515025/springBoot

参考文献: http://www.cnblogs.com/dongying/p/6106855.html http://www.cnblogs.com/dongying/p/6128268.html


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表