上一篇博客
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;}@Overridepublic 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 方法是判定是否拥有权限的决策方法@Overridepublic 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");}@Overridepublic boolean supports(ConfigAttribute attribute) {return true;}@Overridepublic 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 implementsFilterInvocationSecurityMetadataSource {//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。//因为我不想每一次来了请求,都先要匹配一下权限表中的信息是不是包含此url,// 我准备直接拦截,不管请求的url 是什么都直接拦截,然后在MyAccessDecisionManager的decide 方法中做拦截还是放行的决策。//所以此方法的返回值不能返回 null 此处我就随便返回一下。@Overridepublic Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {Collection<ConfigAttribute> co=new ArrayList<>();co.add(new SecurityConfig("null"));return co;}@Overridepublic Collection<ConfigAttribute> getAllConfigAttributes() {return null;}@Overridepublic boolean supports(Class<?> clazz) {return true;}}
5.关闭csrf
关于 什么是csrf 请看我的这篇博客
spring security CSRF 问题
修改 WebSecurityConfig 的configure(HttpSecurity http) 方法 ,添加 .csrf().disable();
@Overrideprotected 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")@ResponseBodypublic String hello(){return "hello admin";}@RequestMapping("/login")public String login(){return "login";}@RequestMapping(value = "/user", method = RequestMethod.GET)@ResponseBodypublic String getList(){return "hello getList";}@RequestMapping(value = "/user", method = RequestMethod.POST)@ResponseBodypublic String save(){return "hello save";}@RequestMapping(value = "/user", method = RequestMethod.PUT)@ResponseBodypublic 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 方法访问失败。
半夜码字。。。
如果本文对您有帮助请给个好评,谢谢。
本文源码:/527515025/springBoot
参考文献:
/dongying/p/6106855.html
/dongying/p/6128268.html