sm整合shiro权限控制
CasUser:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.domain.entity.cas; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.Transient; import com.fasterxml.jackson.annotation.JsonIgnore; import com.micropattern.urp.domain.entity.base.BaseIdAndTime; import com.micropattern.urp.domain.entity.role.Role; /** * cas集成用户<br/> * * @author zuo * @Date 2020年4月20日 上午11:11:05 * @since 1.0.0 * */ @SuppressWarnings("serial") @Table(name = "t_cas_user") @Entity public class CasUser extends BaseIdAndTime{ private String userName; private String role; @Transient private String searchKey; private String salt; private String password; private String remark; /** * 用户角色关系表 */ @JsonIgnore @ManyToMany @JoinTable(name="t_cas_user_role", joinColumns={@JoinColumn(name="user_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}) private Set<Role> roles = new HashSet<>(); public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getRole() { return role; } public void setRole(String role) { this.role = role; } public String getSearchKey() { return searchKey; } public void setSearchKey(String searchKey) { this.searchKey = searchKey; } public Set<Role> getRoles() { return roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
Role:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.domain.entity.role; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonIgnore; import com.micropattern.urp.domain.entity.base.BaseIdAndTime; import com.micropattern.urp.domain.entity.cas.CasUser; import com.micropattern.urp.domain.entity.permission.Permission; /** * 此处应有类说明<br/> * * @author why * @Date 2020年4月13日 下午3:36:58 * @since 1.0.0 * */ @SuppressWarnings("serial") @Table(name = "t_cas_role") @Entity public class Role extends BaseIdAndTime{ /** * 角色名 */ private String name; private Boolean delFlag; private String remark; /** * 角色,用户,多对多 */ @JsonIgnore @ManyToMany(mappedBy="roles") private Set<CasUser> users=new HashSet<>(); @JsonIgnore @ManyToMany @JoinTable(name="t_cas_role_permission", joinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="permission_id",referencedColumnName="id")}) private Set<Permission> permissions = new HashSet<>(); public Set<Permission> getPermissions() { return permissions; } public void setPermissions(Set<Permission> permissions) { this.permissions = permissions; } public Boolean getDelFlag() { return delFlag; } public void setDelFlag(Boolean delFlag) { this.delFlag = delFlag; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<CasUser> getUsers() { return users; } public void setUsers(Set<CasUser> users) { this.users = users; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
Permission:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.domain.entity.permission; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import com.micropattern.urp.domain.entity.base.BaseIdAndTime; /** * 此处应有类说明<br/> * * @author why * @Date 2020年4月13日 下午3:50:53 * @since 1.0.0 * */ @SuppressWarnings("serial") @Table(name = "t_cas_permission") @Entity public class Permission extends BaseIdAndTime{ /** * 权限名称 */ private String name; /** * 权限类型 */ private Integer type; /** * 资源路径 */ private String url; private Boolean delFlag; /** * 是否勾选 * true : 勾选 * false : 未勾选 */ @Transient private Boolean checked; /** * 排序 */ private Integer sort; /** * 菜单等级 * 1:父节点 * 2:子节点 */ private Integer level; /** * 父id */ private String parentId; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getType() { return type; } public void setType(Integer type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Boolean getDelFlag() { return delFlag; } public void setDelFlag(Boolean delFlag) { this.delFlag = delFlag; } public Boolean getChecked() { return checked; } public void setChecked(Boolean checked) { this.checked = checked; } public Integer getSort() { return sort; } public void setSort(Integer sort) { this.sort = sort; } public Integer getLevel() { return level; } public void setLevel(Integer level) { this.level = level; } public String getParentId() { return parentId; } public void setParentId(String parentId) { this.parentId = parentId; } }
建表sql:
CREATE TABLE `t_cas_user` ( `id` varchar(255) NOT NULL COMMENT '用户id', `password` varchar(255) DEFAULT NULL COMMENT '密码', `user_name` varchar(64) DEFAULT NULL COMMENT '用户名', `role` varchar(64) DEFAULT NULL COMMENT '角色', `salt` varchar(64) DEFAULT NULL COMMENT '密码盐', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user` varchar(64) DEFAULT NULL COMMENT '创建用户', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `update_user` varchar(64) DEFAULT NULL COMMENT '更新用户', `version` int(11) DEFAULT '0', `remark` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `t_cas_user_role` ( `user_id` varchar(255) NOT NULL, `role_id` varchar(255) NOT NULL, PRIMARY KEY (`user_id`,`role_id`), KEY `FK_h6p8coxk4oxl6txq1hd7jim82` (`role_id`), CONSTRAINT `FK_h6p8coxk4oxl6txq1hd7jim82` FOREIGN KEY (`role_id`) REFERENCES `t_cas_role` (`id`), CONSTRAINT `FK_oajhcq0g0st27cocwugnq47mf` FOREIGN KEY (`user_id`) REFERENCES `t_cas_user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `t_cas_role` ( `id` varchar(255) NOT NULL, `name` varchar(64) DEFAULT NULL COMMENT '权限名称', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user` varchar(255) DEFAULT NULL COMMENT '创建用户', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `update_user` varchar(255) DEFAULT NULL COMMENT '更新用户', `version` int(11) DEFAULT '0' COMMENT '版本号', `del_flag` bit(1) DEFAULT NULL COMMENT '是否删除,0:未删除', `remark` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表'; CREATE TABLE `t_cas_role_permission` ( `role_id` varchar(255) NOT NULL, `permission_id` varchar(255) NOT NULL, PRIMARY KEY (`role_id`,`permission_id`), KEY `FK_ssde3tsrhwnl5s15n0cys16p0` (`permission_id`), CONSTRAINT `FK_13au68ku0hwd8b01mtpdp49mo` FOREIGN KEY (`role_id`) REFERENCES `t_cas_role` (`id`), CONSTRAINT `FK_ssde3tsrhwnl5s15n0cys16p0` FOREIGN KEY (`permission_id`) REFERENCES `t_cas_permission` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `t_cas_permission` ( `id` varchar(255) NOT NULL COMMENT '主键', `name` varchar(100) DEFAULT NULL COMMENT '权限名称', `type` int(11) DEFAULT NULL COMMENT '权限类型,1:菜单 2:按钮 3:API', `url` varchar(200) DEFAULT NULL COMMENT '资源路径', `create_time` datetime DEFAULT NULL COMMENT '创建时间', `create_user` varchar(255) DEFAULT NULL COMMENT '创建用户', `update_time` datetime DEFAULT NULL COMMENT '更新时间', `update_user` varchar(255) DEFAULT NULL COMMENT '更新用户', `version` int(11) DEFAULT '0' COMMENT '版本号', `del_flag` bit(1) DEFAULT NULL COMMENT '是否删除,0:未删除', `sort` int(11) DEFAULT '0' COMMENT '排序', `parent_id` varchar(64) DEFAULT NULL COMMENT '父id', `level` int(11) DEFAULT NULL COMMENT '权限等级,1:父节点,2:子节点', PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='资源表';
spring-shiro.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-lazy-init="true"> <description>Shiro Configuration</description> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm" /> <property name="cacheManager" ref="cacheManager" /> <!--设置会话管理器--> <!-- <property name="sessionManager" ref="sessionManager"></property> --> </bean> <!-- 項目自定义的Realm --> <bean id="userRealm" class="com.micropattern.urp.common.shiro.UserRealm"> <property name="cacheManager" ref="cacheManager" /> <!-- <property name="credentialsMatcher" ref="credentialsMatcher"></property> --> <property name="credentialsMatcher" ref="customCredentialsMatcher"></property> </bean> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- 用户登录地址 --> <property name="loginUrl" value="/login"/> <!-- 登录成功 --> <property name="successUrl" value="/login"/> <!-- 未授权的失败页 --> <property name="unauthorizedUrl" value="/error"/> <property name="filterChainDefinitions"> <value> <!-- 设置访问用户list页面需要授权操作 --> /** = anon /manage = anon </value> </property> </bean> <!--配置会话管理器--> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!--设置session的超时时间20min--> <property name="globalSessionTimeout" value="1200000"></property> <!--删除失效session--> <property name="deleteInvalidSessions" value="true"></property> </bean> <!-- 给予shior的内存缓存系统 --> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/> <!-- 保证实现了Shiro内部lifecycle函数的bean执行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="SHA-512"></property> <property name="hashIterations" value="2"></property> </bean> --> <bean id="customCredentialsMatcher" class="com.micropattern.urp.common.shiro.CustomCredentialsMatcher"/> </beans>
UserRealm:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.common.shiro; import java.util.HashSet; import java.util.Set; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import com.micropattern.urp.common.constant.ErrorCode.SystemUserError; import com.micropattern.urp.common.enums.SystemUserStatus; import com.micropattern.urp.common.exception.BusinessException; import com.micropattern.urp.domain.entity.permission.Permission; import com.micropattern.urp.domain.entity.role.Role; import com.micropattern.urp.domain.entity.system.User; import com.micropattern.urp.domain.service.system.UserService; /** * shiro管理<br/> * * @author zuo * @Date 2020年4月13日 下午2:10:29 * @since 1.0.0 * */ public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; //授权认证 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("=============执行授权逻辑================"); Set<String> set = new HashSet<>();// 权限集合 // 给资源进行授权 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); User tokenUser = (User) principals.getPrimaryPrincipal(); User user = userService.findByUserName(tokenUser.getUserName()); Set<Role> roles = user.getRoles(); for (Role role : roles) { Set<Permission> permissions = role.getPermissions(); for (Permission permission : permissions) { set.add(permission.getUrl()); } } simpleAuthorizationInfo.addStringPermissions(set); return simpleAuthorizationInfo; } //登录认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=============执行认证逻辑================"); UsernamePasswordToken tokens = (UsernamePasswordToken) token; User user = userService.findByUserName(tokens.getUsername()); if (user == null) { throw new BusinessException(SystemUserError.USER_NAME_NOTEXISTS); } if (user.getDelFlag()) { throw new BusinessException(SystemUserError.USER_NAME_NOTEXISTS); } else if (SystemUserStatus.ENABLE != user.getStatus()) { throw new BusinessException(SystemUserError.LOGIN_FORBID); } SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName()); return simpleAuthenticationInfo; } //自定义密码校验 @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { super.setCredentialsMatcher(new CustomCredentialsMatcher()); } //清空权限缓存 public void clearCachedAuthorization(){ clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); } }
shiro清空缓存:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.common.shiro; import org.apache.shiro.SecurityUtils; import org.apache.shiro.mgt.RealmSecurityManager; /** * shiro清除缓存<br/> * * @author zuo * @Date 2020年4月17日 上午9:25:50 * @since 1.0.0 * */ public class ShiroUtils { public static void clearShiroCache(){ RealmSecurityManager rsm = (RealmSecurityManager)SecurityUtils.getSecurityManager(); UserRealm realm = (UserRealm)rsm.getRealms().iterator().next(); realm.clearCachedAuthorization(); } }
自定义加密类:
/** * Copyright (c) 2018, All Rights Reserved. * */ package com.micropattern.urp.common.utils; import java.security.MessageDigest; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 签名加密工具类<br/> * Date: 2018年1月29日 下午2:15:59 <br/> * * @author xin.zhou * @version * @since JDK 1.7 * @see */ public class SignUtils { private static final Logger LOG = LoggerFactory.getLogger(SignUtils.class); private static final String DEFAULT_CHARSET = "UTF-8"; private static final char[] DIGITS; public static String hmacSha256(String key, String data) { try { Mac mac = Mac.getInstance("HmacSHA256"); SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), mac.getAlgorithm()); mac.init(signingKey); return encodeHex(mac.doFinal(data.getBytes())); } catch (Exception e) { LOG.error("execute hmacSHA256 error", e); } return null; } private static String encrypt(String algorithm, String data, String charset) { try { byte[] msg = data.getBytes(charset); MessageDigest md = MessageDigest.getInstance(algorithm); return encodeHex(md.digest(msg)); } catch (Exception e) { LOG.error(e.getMessage(), e); } return null; } public static String md5(String data, String charset) { return encrypt("MD5", data, charset); } public static String sha1(String data, String charset) { return encrypt("SHA1", data, charset); } public static String sha1(String data) { return sha1(data, DEFAULT_CHARSET); } public static String sha256(String data, String charset) { return encrypt("SHA-256", data, charset); } public static String sha256(String data) { return sha256(data, DEFAULT_CHARSET); } public static String sha512(String data, String charset) { return encrypt("SHA-512", data, charset); } public static String sha512(String data) { return sha512(data, DEFAULT_CHARSET); } private static String encodeHex(byte[] data) { int l = data.length; char[] out = new char[l << 1]; int i = 0; for (int j = 0; i < l; ++i) { out[j++] = DIGITS[(240 & data[i]) >>> 4]; out[j++] = DIGITS[15 & data[i]]; } return new String(out); } static { DIGITS = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; } public static void main(String[] args) { //UPJW1u //1d0c7526881640dfb2a7018c9e2328a3b520b6236bf53378b42059578cc603ffa81c159384119c5bcf280d16503abe32d747104e7c409d53c90ced1e7136fe10 String salt=RandomStringUtils.randomAlphanumeric(6); System.out.println(salt+" "+sha512("123456"+salt)); } }
shiro加密校验:
/** * Copyright (c) 2020, All Rights Reserved. * */ package com.micropattern.urp.common.shiro; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.SimpleCredentialsMatcher; import org.apache.shiro.subject.PrincipalCollection; import com.micropattern.urp.common.utils.SignUtils; import com.micropattern.urp.domain.entity.system.User; /** * 自定义shiro密码校验<br/> * * @author zuo * @Date 2020年4月14日 下午5:30:44 * @since 1.0.0 * */ public class CustomCredentialsMatcher extends SimpleCredentialsMatcher { @Override public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) { UsernamePasswordToken token = (UsernamePasswordToken) authcToken; PrincipalCollection principals = info.getPrincipals(); User user = (User) principals.getPrimaryPrincipal(); String salt = user.getSalt(); String password = String.valueOf(token.getPassword()); Object tokenCredentials = encrypt(password, salt); Object accountCredentials = getCredentials(info); //将密码加密与系统加密后的密码校验,内容一致就返回true,不一致就返回false return equals(tokenCredentials, accountCredentials); } //密码加密方法 private String encrypt(String password, String salt) { return SignUtils.sha512(password + salt); } }
页面按钮控制:
<@shiro.hasPermission name="sysuser:add">
<button class="btn btn-primary btn-xs" id="addBtn"><i class="ace-icon glyphicon glyphicon-plus"></i>新增</button>
</@shiro.hasPermission>