项目需求,需要管理员动态配置相关url的权限,网上找的大多不太理想,自己参照某些实现:
当权限变更时,需要实时刷新权限
技术栈:
## springboot 2.0 集成 jpa ### 环境: * 开发工具:Intellij IDEA 2017.1.3 * springboot: **2.0.1.RELEASE** * jdk:1.8.0_40 * maven:3.3.9 * alibaba Druid 数据库连接池:1.1.9
JWT配置:
jwt:
# secret: mySecret
# #token有效期一天
expiration: 86400
# 加密秘钥
secret: f4e2e52034348f86b67cde581c0f9eb5[jwuwb]
# token有效时长,1天,单位秒
expire: 86400
header: token
import com.jwuwb.oa.entity.SysUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* jwt工具类
* @author zf
*/
@ConfigurationProperties(prefix = "jwt")
@Component
public class JwtUtil {
private Logger logger = LoggerFactory.getLogger(getClass());
private String secret;
private long expire;
private String header;
/**
* 生成jwt token
*/
public String generateToken(String userName) {
Date nowDate = new Date();
//过期时间
Date expireDate = new Date(nowDate.getTime() + expire * 1000);
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(userName)
.setIssuedAt(nowDate)
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public Claims getClaimByToken(String token) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
logger.debug("validate is token error ", e);
return null;
}
}
/**
* token是否过期
* @return true:过期
*/
public boolean isTokenExpired(Date expiration) {
return expiration.before(new Date());
}
public Boolean validateToken(String token, UserDetails userDetails) {
SysUser user = (SysUser) userDetails;
final String username = getClaimByToken(token).getSubject();
return (username.equals(user.getUsername())
&& !isTokenExpired(token)
);
}
private Boolean isTokenExpired(String token) {
final Date expiration = getClaimByToken(token).getExpiration();
return expiration.before(new Date());
}
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public long getExpire() {
return expire;
}
public void setExpire(long expire) {
this.expire = expire;
}
public String getHeader() {
return header;
}
public void setHeader(String header) {
this.header = header;
}
}
前置过滤:
import com.google.common.collect.Maps;
import com.jwuwb.oa.model.PermissionMap;
import com.jwuwb.oa.model.PermissionVo;
import com.jwuwb.oa.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
/**
* 判断是否具有权限访问当前资源
* @author zf
*/
@Component("perauthorityservice")
public class PerAuthorityService {
@Autowired
private PermissionService permissionService;
/**
* 判断是否有权限
*
* @param request
* @param authentication
* @return
*/
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Collection collection = getAttributes(request);
if (authentication.getPrincipal().equals("anonymousUser")) {
return false;
}
if (null == collection || collection.size() <= 0) {
return true;
}
ConfigAttribute configAttribute;
String needRole;
for (Iterator iterator = collection.iterator(); iterator.hasNext(); ) {
configAttribute = iterator.next();
needRole = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
String authority = grantedAuthority.getAuthority();
if (authority.contains(needRole.trim())) {
return true;
}
}
}
throw new AccessDeniedException("权限不足");
}
/**
* 判定用户请求的url是否在权限表中,如果在权限表中,则返回decide方法,
* 用来判定用户是否有权限,如果不在权限表中则放行
*
* @param request
* @return
* @throws IllegalArgumentException
*/
public Collection getAttributes(HttpServletRequest request) throws IllegalArgumentException {
HashMap> map = PermissionMap.map;
if (map == null) {
map = loadResourceDefine();
}
for (Map.Entry> entry : map.entrySet()) {
String url = entry.getKey();
if (new AntPathRequestMatcher(url).matches(request)) {
return map.get(url);
}
}
return null;
}
/**
* 加载权限表中所有权限
*/
private HashMap> loadResourceDefine() {
HashMap> map = Maps.newHashMap();
List all = permissionService.listAllVo();
for (PermissionVo vo : all) {
List configAttributeList = vo.getRoleNames().stream().map(roleName -> {
ConfigAttribute configAttribute = new SecurityConfig("ROLE_" + roleName.toUpperCase());
return configAttribute;
}).collect(Collectors.toList());
map.put(vo.getUrl(), configAttributeList);
}
PermissionMap.map = map;
return map;
}
}
覆写登录后回调:
import com.google.common.collect.Lists;
import com.jwuwb.oa.base.util.StringUtil;
import com.jwuwb.oa.dao.SysUserDao;
import com.jwuwb.oa.entity.Permission;
import com.jwuwb.oa.entity.SysRole;
import com.jwuwb.oa.entity.SysUser;
import com.jwuwb.oa.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 覆写登录存储权限
* @author zf
* @date 2019/12/7
*/
@Service
public class UrlUserService implements UserDetailsService {
@Autowired
SysUserDao userDao;
@Autowired
PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String userName) {
//重写loadUserByUsername 方法获得 userdetails 类型用户
SysUser user = userDao.findByUsername(userName);
if (user != null) {
Set roleList = user.getRoleList();
List list = Lists.newArrayList();
for (SysRole role : roleList) {
list.add("ROLE_" + role.getName());
}
String roles = StringUtil.transListToString(list);
// Integer userId = user.getId();
// //拉取权限和角色信息
// List permissions = permissionService.listByUserId(userId);
List grantedAuthorities = new ArrayList<>();
GrantedAuthority grantedAuthority = new UrlGrantedAuthority(roles);
grantedAuthorities.add(grantedAuthority);
// if (permissions != null){
//
// for (Permission permission : permissions) {
// if (permission != null && permission.getName()!=null) {
// //填充角色和权限
// GrantedAuthority grantedAuthority = new UrlGrantedAuthority(permission.getUrl(),
// permission.getMethod());
// grantedAuthorities.add(grantedAuthority);
// }
// }
// }
user.setGrantedAuthorities(grantedAuthorities);
return user;
} else {
throw new UsernameNotFoundException("admin: " + userName + " do not exist!");
}
}
}
涉及到的实体类:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jwuwb.oa.base.util.StringUtil;
import com.jwuwb.oa.entity.SysRole;
import org.springframework.security.core.GrantedAuthority;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
*
* @author zf
* @date 2019/12/7
*/
public class UrlGrantedAuthority implements GrantedAuthority {
private String roles;
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
private String permissionUrl;
private String method;
public String getPermissionUrl() {
return permissionUrl;
}
public void setPermissionUrl(String permissionUrl) {
this.permissionUrl = permissionUrl;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public UrlGrantedAuthority (String permissionUrl, String method) {
this.permissionUrl = permissionUrl;
this.method = method;
}
public UrlGrantedAuthority (String roles) {
this.roles = roles;
}
@Override
public String getAuthority() {
return this.roles;
}
// @Override
// public String getAuthority() {
// return this.permissionUrl + ";" + this.method;
// }
}
用户对象:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.jwuwb.oa.base.comm.BaseEntity;
import com.jwuwb.oa.base.comm.BeanCopyUtil;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.Collection;
import java.util.List;
import java.util.Set;
/**
* @program: oa
* @description: 角色
* @author: zf
* @create: 2019-12-04 15:25
**/
@EqualsAndHashCode(callSuper = true)
@Data
@Entity
@Table(name = "jw_user")
@Accessors(chain = true)
@DynamicInsert
@DynamicUpdate
public class SysUser extends BaseEntity implements UserDetails {
@ApiParam(value = "用户名称")
private String username;
@ApiParam(value = "密码")
@JsonIgnore
private String password;
@ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
@JoinTable(
name = "user_role",
joinColumns = {@JoinColumn(name = "uid", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "rid", referencedColumnName = "id")})
private Set roleList;
@ApiParam(value = "关联部门", example = "10")
private Integer deptId;
@ApiParam(value = "职务")
private String post;
@ApiParam(value = "昵称")
private String nickname;
@ApiParam(value = "部门名称", hidden = true)
private String deptName;
@ApiParam(value = "是否是超管用户", hidden = true)
private Boolean isSuper;
@ApiParam(value = "是否启用", hidden = true)
private Boolean enabled;
@Transient
@ApiParam(hidden = true)
@JsonIgnore
private String roleCode;
@Transient
@ApiParam(hidden = true)
private List extends GrantedAuthority> authorities;
@Override
@JsonIgnore
public boolean isAccountNonExpired() {
return true;
}
@Override
@JsonIgnore
public Collection extends GrantedAuthority> getAuthorities() {
return authorities;
}
@JsonIgnore
public void setGrantedAuthorities(List extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
@JsonIgnore
public boolean isAccountNonLocked() {
return true;
}
@Override
@JsonIgnore
public boolean isCredentialsNonExpired() {
return true;
}
@Override
@JsonIgnore
public boolean isEnabled() {
return true;
}
public void copy(SysUser et) {
BeanCopyUtil.beanCopyWithIngore(et, this);
}
}
角色对象:
import com.jwuwb.oa.base.comm.BaseEntity;
import com.jwuwb.oa.base.comm.BeanCopyUtil;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.util.Set;
/**
* @program: oa
* @description: 角色
* @author: zf
* @create: 2019-12-04 15:25
**/
@EqualsAndHashCode(callSuper = true)
@Data
@Entity
@Table(name = "jw_role")
@Accessors(chain = true)
@DynamicInsert
@DynamicUpdate
public class SysRole extends BaseEntity {
@ApiParam(value = "角色名称")
private String name;
@ManyToMany(cascade = CascadeType.DETACH, fetch = FetchType.EAGER)
@JoinTable(
name = "role_permission",
joinColumns = {@JoinColumn(name = "rid", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "pid", referencedColumnName = "id")})
private Set permissions;
@ApiParam(value = "角色代码")
private String roleCode;
public void copy(SysRole et) {
BeanCopyUtil.beanCopyWithIngore(et, this);
}
}
权限资源:
import com.jwuwb.oa.base.comm.BaseEntity;
import com.jwuwb.oa.base.comm.BeanCopyUtil;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* @program: oa
* @description: 权限
* @author: zf
* @create: 2019-12-04 15:25
**/
@EqualsAndHashCode(callSuper = true)
@Data
@Entity
@Table(name = "jw_permission")
@Accessors(chain = true)
@DynamicInsert
@DynamicUpdate
public class Permission extends BaseEntity {
@ApiParam(value = "权限名称")
private String name;
@ApiParam(value = "权限描述")
private String descritpion;
@ApiParam(value = "授权链接")
private String url;
@ApiParam(value = "父节点id")
private int pid;
@ApiParam(value = "请求方法")
private String method;
public void copy(Permission et) {
BeanCopyUtil.beanCopyWithIngore(et, this);
}
}
权限资源url 设置规则:/xx/** 或者/xx/xx