上一篇博客
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.
*/
@Service
public 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.
*/
@Service
public 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 请看我的这篇博客
修改 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.
*/
@Controller
public 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