主框架:springboot
响应层:springMVC
持久层:mybatis
事务控制:jta
前端技术:easyui
sh_user:用户表,一个用户可以有多个角色
sh_role:角色表,一个角色可以有多个资源
sh_resource:资源表
sh_user_role:用户角色中间表
sh_role_resource:角色资源中间表
sh_user
CREATE TABLE `sh_user` (
`ID` varchar(36) NOT NULL COMMENT '主键',
`LOGIN_NAME` varchar(36) DEFAULT NULL COMMENT '登录名称',
`REAL_NAME` varchar(36) DEFAULT NULL COMMENT '真实姓名',
`NICK_NAME` varchar(36) DEFAULT NULL COMMENT '昵称',
`PASS_WORD` varchar(150) DEFAULT NULL COMMENT '密码',
`SALT` varchar(36) DEFAULT NULL COMMENT '加密因子',
`SEX` int(11) DEFAULT NULL COMMENT '性别',
`ZIPCODE` varchar(36) DEFAULT NULL COMMENT '邮箱',
`ADDRESS` varchar(36) DEFAULT NULL COMMENT '地址',
`TEL` varchar(36) DEFAULT NULL COMMENT '固定电话',
`MOBIL` varchar(36) DEFAULT NULL COMMENT '电话',
`EMAIL` varchar(36) DEFAULT NULL COMMENT '邮箱',
`DUTIES` varchar(36) DEFAULT NULL COMMENT '职务',
`SORT_NO` int(11) DEFAULT NULL COMMENT '排序',
`ENABLE_FLAG` varchar(18) DEFAULT NULL COMMENT '是否有效',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户表';
sh_role
CREATE TABLE `sh_role` (
`ID` varchar(36) NOT NULL COMMENT '主键',
`ROLE_NAME` varchar(36) DEFAULT NULL COMMENT '角色名称',
`LABEL` varchar(36) DEFAULT NULL COMMENT '角色标识',
`DESCRIPTION` varchar(200) DEFAULT NULL COMMENT '角色描述',
`SORT_NO` int(36) DEFAULT NULL COMMENT '排序',
`ENABLE_FLAG` varchar(18) DEFAULT NULL COMMENT '是否有效',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户角色表';
sh_resource
CREATE TABLE `sh_resource` (
`ID` varchar(36) NOT NULL COMMENT '主键',
`PARENT_ID` varchar(36) DEFAULT NULL COMMENT '父资源',
`RESOURCE_NAME` varchar(36) DEFAULT NULL COMMENT '资源名称',
`REQUEST_PATH` varchar(200) DEFAULT NULL COMMENT '资源路径',
`LABEL` varchar(200) DEFAULT NULL COMMENT '资源标签',
`ICON` varchar(20) DEFAULT NULL COMMENT '图标',
`IS_LEAF` varchar(18) DEFAULT NULL COMMENT '是否叶子节点',
`RESOURCE_TYPE` varchar(36) DEFAULT NULL COMMENT '资源类型',
`SORT_NO` int(11) DEFAULT NULL COMMENT '排序',
`DESCRIPTION` varchar(200) DEFAULT NULL COMMENT '描述',
`SYSTEM_CODE` varchar(36) DEFAULT NULL COMMENT '系统code',
`IS_SYSTEM_ROOT` varchar(18) DEFAULT NULL COMMENT '是否根节点',
`ENABLE_FLAG` varchar(18) DEFAULT NULL COMMENT '是否有效',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='资源表';
sh_role_resource
CREATE TABLE `sh_role_resource` (
`ID` varchar(36) NOT NULL,
`ENABLE_FLAG` varchar(18) DEFAULT NULL,
`ROLE_ID` varchar(36) DEFAULT NULL,
`RESOURCE_ID` varchar(36) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色资源表';
sh_user_role
CREATE TABLE `sh_user_role` (
`ID` varchar(36) NOT NULL,
`ENABLE_FLAG` varchar(18) DEFAULT NULL,
`USER_ID` varchar(36) DEFAULT NULL,
`ROLE_ID` varchar(36) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户角色表';
(1)、ShiroDbRealmImpl继承ShiroDbRealm向上继承AuthorizingRealm,ShiroDbRealmImpl实例化时会创建密码匹配器HashedCredentialsMatcher实例,HashedCredentialsMatcher指定hash次数与方式,交于AuthenticatingRealm
(2)、调用login方法后,最终调用doGetAuthenticationInfo(AuthenticationToken authcToken)方法,拿到SimpleToken的对象,调用UserBridgeService的查找用户方法,把ShiroUser对象、密码和salt交于SimpleAuthenticationInfo去认证
(3)、访问需要鉴权时,调用doGetAuthorizationInfo(PrincipalCollection principals)方法,然后调用UserBridgeService的授权验证
package com.itheima.shiro.core;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import javax.annotation.PostConstruct;
/**
*
* @Description shiro自定义realm
*/
public abstract class ShiroDbRealm extends AuthorizingRealm {
/**
* @Description 认证
* @param authcToken token对象
* @return
*/
public abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) ;
/**
* @Description 鉴权
* @param principals 令牌
* @return
*/
public abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals);
/**
* @Description 密码匹配器
*/
@PostConstruct
public abstract void initCredentialsMatcher() ;
}
package com.itheima.shiro.core.impl;
import com.itheima.shiro.constant.SuperConstant;
import com.itheima.shiro.core.base.ShiroUser;
import com.itheima.shiro.core.base.SimpleToken;
import com.itheima.shiro.core.ShiroDbRealm;
import com.itheima.shiro.core.bridge.UserBridgeService;
import com.itheima.shiro.pojo.User;
import com.itheima.shiro.utils.BeanConv;
import com.itheima.shiro.utils.DigestsUtil;
import com.itheima.shiro.utils.EmptyUtil;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @Description:自定义shiro的实现
*/
public class ShiroDbRealmImpl extends ShiroDbRealm {
@Autowired
private UserBridgeService userBridgeService;
/**
* @Description 认证方法
* @param authcToken 校验传入令牌
* @return AuthenticationInfo
*/
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {
SimpleToken token = (SimpleToken)authcToken;
User user = userBridgeService.findUserByLoginName(token.getUsername());
if(EmptyUtil.isNullOrEmpty(user)){
throw new UnknownAccountException("账号不存在");
}
ShiroUser shiroUser = BeanConv.toBean(user, ShiroUser.class);
shiroUser.setResourceIds(userBridgeService.findResourcesIdsList(user.getId()));
String salt = user.getSalt();
String password = user.getPassWord();
return new SimpleAuthenticationInfo(shiroUser, password, ByteSource.Util.bytes(salt), getName());
}
/**
* @Description 授权方法
* @param principals SimpleAuthenticationInfo对象第一个参数
* @return
*/
@Override
public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
return userBridgeService.getAuthorizationInfo(shiroUser);
}
/**
* @Description 加密方式
*/
@Override
public void initCredentialsMatcher() {
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(SuperConstant.HASH_ALGORITHM);
matcher.setHashIterations(SuperConstant.HASH_INTERATIONS);
setCredentialsMatcher(matcher);
}
}
package com.itheima.shiro.core.base;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* @Description 自定义tooken
*/
public class SimpleToken extends UsernamePasswordToken {
/** serialVersionUID */
private static final long serialVersionUID = -4849823851197352099L;
private String tokenType;
private String quickPassword;
/**
* Constructor for SimpleToken
* @param tokenType
*/
public SimpleToken(String tokenType, String username,String password) {
super(username,password);
this.tokenType = tokenType;
}
public SimpleToken(String tokenType, String username,String password,String quickPassword) {
super(username,password);
this.tokenType = tokenType;
this.quickPassword = quickPassword;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getQuickPassword() {
return quickPassword;
}
public void setQuickPassword(String quickPassword) {
this.quickPassword = quickPassword;
}
}
package com.itheima.shiro.core.base;
import com.itheima.shiro.utils.ToString;
import lombok.Data;
import java.util.List;
/**
* @Description 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
*/
@Data
public class ShiroUser extends ToString {
/** serialVersionUID */
private static final long serialVersionUID = -5024855628064590607L;
/**
* 主键
*/
private String id;
/**
* 登录名称
*/
private String loginName;
/**
* 真实姓名
*/
private String realName;
/**
* 昵称
*/
private String nickName;
/**
* 密码
*/
private String passWord;
/**
* 加密因子
*/
private String salt;
/**
* 性别
*/
private Integer sex;
/**
* 邮箱
*/
private String zipcode;
/**
* 地址
*/
private String address;
/**
* 固定电话
*/
private String tel;
/**
* 电话
*/
private String mobil;
/**
* 邮箱
*/
private String email;
/**
* 职务
*/
private String duties;
/**
* 排序
*/
private Integer sortNo;
/**
* 是否有效
*/
private String enableFlag;
private List<String> resourceIds;
public ShiroUser() {
super();
}
public ShiroUser(String id, String loginName) {
super();
this.id = id;
this.loginName = loginName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result
+ ((loginName == null) ? 0 : loginName.hashCode());
result = prime * result + ((mobil == null) ? 0 : mobil.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ShiroUser other = (ShiroUser) obj;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (loginName == null) {
if (other.loginName != null)
return false;
} else if (!loginName.equals(other.loginName))
return false;
if (mobil == null) {
if (other.mobil != null)
return false;
} else if (!mobil.equals(other.mobil))
return false;
return true;
}
}
package com.itheima.shiro.core.bridge;
import com.itheima.shiro.core.base.ShiroUser;
import com.itheima.shiro.pojo.User;
import org.apache.shiro.authz.AuthorizationInfo;
import java.util.List;
/**
* @Description:用户信息桥接(后期会做缓存)
*/
public interface UserBridgeService {
/**
* @Description 查找用户信息
* @param loginName 用户名称
* @return user对象
*/
User findUserByLoginName(String loginName);
/**
* @Description 鉴权方法
* @param shiroUser 令牌对象
* @return 鉴权信息
*/
AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser);
/**
* @Description 查询用户对应角色标识list
* @param userId 用户id
* @return 角色标识集合
*/
List<String> findRoleList(String userId);
/**
* @Description 查询用户对应资源标识list
* @param userId 用户id
* @return 资源标识集合
*/
List<String> findResourcesList(String userId);
/**
* @Description 查询资源ids
* @param userId 用户id
* @return 资源id集合
*/
List<String> findResourcesIds(String userId);
}
package com.itheima.shiro.core.bridge.impl;
import com.itheima.shiro.core.adapter.UserAdapter;
import com.itheima.shiro.core.base.ShiroUser;
import com.itheima.shiro.core.bridge.UserBridgeService;
import com.itheima.shiro.pojo.Resource;
import com.itheima.shiro.pojo.Role;
import com.itheima.shiro.pojo.User;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @Description:用户信息桥接(后期会做缓存)
*/
@Component("userBridgeService")
public class UserBridgeServiceImpl implements UserBridgeService {
@Autowired
UserAdapter userAdapter;
@Override
public User findUserByLoginName(String loginName) {
return userAdapter.findUserByLoginName(loginName);
}
@Override
public AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser) {
//查询用户对应的角色标识
List<String> roleList = this.findRoleList(shiroUser.getId());
//查询用户对于的资源标识
List<String> resourcesList = this.findResourcesList(shiroUser.getId());
//构建鉴权信息对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(roleList);
simpleAuthorizationInfo.addStringPermissions(resourcesList);
return simpleAuthorizationInfo;
}
@Override
public List<String> findRoleList(String userId){
List<Role> roles = userAdapter.findRoleByUserId(userId);
List<String> roleLabel = new ArrayList<>();
for (Role role : roles) {
roleLabel.add(role.getLabel());
}
return roleLabel;
}
@Override
public List<String> findResourcesList(String userId){
List<Resource> resources = userAdapter.findResourceByUserId(userId);
List<String> resourceLabel = new ArrayList<>();
for (Resource resource : resources) {
resourceLabel.add(resource.getLabel());
}
return resourceLabel;
}
@Override
public List<String> findResourcesIds(String userId) {
List<Resource> resources = userAdapter.findResourceByUserId(userId);
List<String> ids = new ArrayList<>();
for (Resource resource : resources) {
ids.add(resource.getId());
}
return ids;
}
}
package com.itheima.shiro.core.adapter;
import com.itheima.shiro.pojo.Resource;
import com.itheima.shiro.pojo.Role;
import com.itheima.shiro.pojo.User;
import java.util.List;
/**
* @Description 后台登陆用户适配器接口
*/
public interface UserAdapter {
/**
* @Description 按用户名查找用户
* @param loginName 登录名
* @return
*/
User findUserByLoginName(String loginName);
/**
* @Description 查找用户所有角色
* @param userId 用户Id
* @return
*/
List<Role> findRoleByUserId(String userId);
/**
* @Description 查询用户有那些资源
* @param userId 用户Id
* @return
*/
List<Resource> findResourceByUserId(String userId);
}
package com.itheima.shiro.core.adapter.impl;
import com.itheima.shiro.constant.SuperConstant;
import com.itheima.shiro.core.adapter.UserAdapter;
import com.itheima.shiro.mapper.UserMapper;
import com.itheima.shiro.mappercustom.UserAdapterMapper;
import com.itheima.shiro.pojo.Resource;
import com.itheima.shiro.pojo.Role;
import com.itheima.shiro.pojo.User;
import com.itheima.shiro.pojo.UserExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description 后台登陆用户适配器接口实现
*/
@Component("userAdapter")
public class UserAdapterImpl implements UserAdapter {
@Autowired
private UserMapper userMapper;
@Autowired
private UserAdapterMapper userAdapterMapper;
@Override
public User findUserByLoginName(String loginName) {
UserExample userExample = new UserExample();
userExample.createCriteria().andEnableFlagEqualTo(SuperConstant.YES).andLoginNameEqualTo(loginName);
List<User> userList = userMapper.selectByExample(userExample);
if (userList.size()==1) {
return userList.get(0);
}else {
return null;
}
}
@Override
public List<Role> findRoleByUserId(String userId) {
Map<String, Object> values = new HashMap<String, Object>();
values.put("userId", userId);
values.put("enableFlag", SuperConstant.YES);
List<Role> list = userAdapterMapper.findRoleByUserId(values);
return list;
}
@Override
public List<Resource> findResourceByUserId(String userId) {
Map<String, Object> values = new HashMap<String, Object>();
values.put("userId", userId);
values.put("enableFlag", SuperConstant.YES);
List<Resource> list=userAdapterMapper.findResourceByUserId(values);
return list;
}
}
(1)、创建SimpleCookie,访问项目时,会在客户端中cookie中存放ShiroSession的对
(2)、创建DefaultWebSessionManager会话管理器定义cookie机制、定时刷新、全局会话超时时间然后交
于DefaultWebSecurityManager权限管理器管理
(3)、创建自定义ShiroDbRealm实现,用于权限认证、授权、加密方式的管理,同时从数据库中取得相关的
角色、资源、用户的信息,然后交于DefaultWebSecurityManager权限管理器管理
(4)、创建DefaultWebSecurityManager权限管理器用于管理DefaultWebSessionManager会话管理器、ShiroDbRealm
(5)、创建lifecycleBeanPostProcessor和DefaultAdvisorAutoProxyCreator相互配合事项注解的权限鉴权
(6)、创建ShiroFilterFactoryBean的shiro过滤器指定权限管理器、同时启动连接链及登录URL、未登录的URL
的跳转
package com.itheima.shiro.config;
import com.itheima.shiro.core.ShiroDbRealm;
import com.itheima.shiro.core.impl.ShiroDbRealmImpl;
import com.itheima.shiro.properties.PropertiesUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @Description:权限配置类
*/
@Configuration
@ComponentScan(basePackages = "com.itheima.shiro.core")
@Log4j2
public class ShiroConfig {
/**
* @Description 创建cookie对象
*/
@Bean(name="sessionIdCookie")
public SimpleCookie simpleCookie(){
SimpleCookie simpleCookie = new SimpleCookie();
simpleCookie.setName("ShiroSession");
return simpleCookie;
}
/**
* @Description 权限管理器
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroDbRealm());
securityManager.setSessionManager(shiroSessionManager());
return securityManager;
}
/**
* @Description 自定义RealmImpl
*/
@Bean(name="shiroDbRealm")
public ShiroDbRealm shiroDbRealm(){
return new ShiroDbRealmImpl();
}
/**
* @Description 会话管理器
*/
@Bean(name="sessionManager")
public DefaultWebSessionManager shiroSessionManager(){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(false);
sessionManager.setSessionIdCookieEnabled(true);
sessionManager.setSessionIdCookie(simpleCookie());
sessionManager.setGlobalSessionTimeout(3600000);
return sessionManager;
}
/**
* @Description 保证实现了Shiro内部lifecycle函数的bean执行
*/
@Bean(name = "lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* @Description AOP式方法级权限检查
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/**
* @Description 配合DefaultAdvisorAutoProxyCreator事项注解权限校验
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(defaultWebSecurityManager());
return new AuthorizationAttributeSourceAdvisor();
}
/**
* @Description 过滤器链
*/
private Map<String, String> filterChainDefinition(){
List<Object> list = PropertiesUtil.propertiesShiro.getKeyList();
Map<String, String> map = new LinkedHashMap<>();
for (Object object : list) {
String key = object.toString();
String value = PropertiesUtil.getShiroValue(key);
log.info("读取防止盗链控制:---key{},---value:{}",key,value);
map.put(key, value);
}
return map;
}
/**
* @Description Shiro过滤器
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(defaultWebSecurityManager());
shiroFilter.setFilterChainDefinitionMap(filterChainDefinition());
shiroFilter.setLoginUrl("/login");
shiroFilter.setUnauthorizedUrl("/login");
return shiroFilter;
}
}
Shiro内置了很多默认的过滤器,比如身份验证、授权等相关的。默认过滤器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的枚举过滤器
定义:authentication.properties
#静态资源不过滤
/static/**=anon
#登录链接不过滤
/login/**=anon
#其他链接是需要登录的
/**=authc
注意:这里定义的过滤器是有执行顺序的,从上向下执行
定义:PropertiesUtil,从classpath中加载authentication.properties
package com.itheima.shiro.properties;
import com.itheima.shiro.utils.EmptyUtil;
import lombok.extern.log4j.Log4j2;
/**
* @Description 读取Properties的工具类
*/
@Log4j2
public class PropertiesUtil {
public static LinkProperties propertiesShiro = new LinkProperties();
/**
* 读取properties配置文件信息
*/
static {
String sysName = System.getProperty("sys.name");
if (EmptyUtil.isNullOrEmpty(sysName)) {
sysName = "application.properties";
} else {
sysName += ".properties";
}
try {
propertiesShiro.load(PropertiesUtil.class.getClassLoader()
.getResourceAsStream("authentication.properties"));
} catch (Exception e) {
log.warn("资源路径中不存在authentication.properties权限文件,忽略读取!");
}
}
/**
* 根据key得到value的值
*/
public static String getShiroValue(String key) {
return propertiesShiro.getProperty(key);
}
}
定义LinkProperties,这个类保证了Properties类的有序
package com.itheima.shiro.properties;
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
/**
* @Description 有序Properties类
*/
public class LinkProperties extends Properties{
/** serialVersionUID */
private static final long serialVersionUID = 7573016303908223266L;
private List<Object> keyList = new ArrayList<Object>();
/**
* 默认构造方法
*/
public LinkProperties() {
}
/**
* 从指定路径加载信息到Properties
* @param path
*/
public LinkProperties(String path) {
try {
InputStream is = new FileInputStream(path);
this.load(is);
} catch (FileNotFoundException e) {
e.printStackTrace();
throw new RuntimeException("指定文件不存在!");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 重写put方法,按照property的存入顺序保存key到keyList,遇到重复的后者将覆盖前者。
*/
@Override
public synchronized Object put(Object key, Object value) {
this.removeKeyIfExists(key);
keyList.add(key);
return super.put(key, value);
}
/**
* 重写remove方法,删除属性时清除keyList中对应的key。
*/
@Override
public synchronized Object remove(Object key) {
this.removeKeyIfExists(key);
return super.remove(key);
}
/**
* keyList中存在指定的key时则将其删除
*/
private void removeKeyIfExists(Object key) {
keyList.remove(key);
}
/**
* 获取Properties中key的有序集合
* @return
*/
public List<Object> getKeyList() {
return keyList;
}
/**
* 保存Properties到指定文件,默认使用UTF-8编码
* @param path 指定文件路径
*/
public void store(String path) {
this.store(path, "UTF-8");
}
/**
* 保存Properties到指定文件,并指定对应存放编码
* @param path 指定路径
* @param charset 文件编码
*/
public void store(String path, String charset) {
if (path != null && !"".equals(path)) {
try {
OutputStream os = new FileOutputStream(path);
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, charset));
this.store(bw, null);
bw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
} else {
throw new RuntimeException("存储路径不能为空!");
}
}
/**
* 重写keys方法,返回根据keyList适配的Enumeration,且保持HashTable keys()方法的原有语义,
* 每次都调用返回一个新的Enumeration对象,且和之前的不产生冲突
*/
@Override
public synchronized Enumeration<Object> keys() {
return new EnumerationAdapter<Object>(keyList);
}
/**
* List到Enumeration的适配器
*/
private class EnumerationAdapter<T> implements Enumeration<T> {
private int index = 0;
private final List<T> list;
private final boolean isEmpty;
public EnumerationAdapter(List<T> list) {
this.list = list;
this.isEmpty = list.isEmpty();
}
public boolean hasMoreElements() {
//isEmpty的引入是为了更贴近HashTable原有的语义,在HashTable中添加元素前调用其keys()方法获得一个Enumeration的引用,
//之后往HashTable中添加数据后,调用之前获取到的Enumeration的hasMoreElements()将返回false,但如果此时重新获取一个
//Enumeration的引用,则新Enumeration的hasMoreElements()将返回true,而且之后对HashTable数据的增、删、改都是可以在
//nextElement中获取到的。
return !isEmpty && index < list.size();
}
public T nextElement() {
if (this.hasMoreElements()) {
return list.get(index++);
}
return null;
}
}
}
查看shirocConfig
加载完整之后交于ShiroFilterFactoryBean使用setFilterChainDefinitionMap使得过滤生效
上面我们使用了shiro的默认过滤器,但是由于业务需求,咱们可能要定义自己的过滤器,那么咱们定义呢?
这里我们先查看RolesAuthorizationFilter
分析:改源码表示,例如:/admin/order= roles["admin, root"] ,只有当放问该接口同时具备admin和root两种角色时,才可以被访问。
1、实现只要有其中一个角色,则可访问对应路径
新建filter层,新建类RolesOrAuthorizationFilter
package com.itheima.shiro.filter;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.Set;
/**
* @Description:角色或关系
*/
public class RolesOrAuthorizationFilter extends AuthorizationFilter {
//TODO - complete JavaDoc
@SuppressWarnings({"unchecked"})
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;
if (rolesArray == null || rolesArray.length == 0) {
//no roles specified, so nothing to check - allow access.
return true;
}
Set<String> roles = CollectionUtils.asSet(rolesArray);
//循环roles判断只要有角色则返回true
for (String role : roles) {
if(subject.hasRole(role)){
return true;
}
}
return false;
}
}
在ShiroConfig类中添加如下内容
/**
* @Description 自定义过滤器定义
*/
private Map<String, Filter> filters() {
Map<String, Filter> map = new HashMap<String, Filter>();
map.put("role-or", new RolesOrAuthorizationFilter());
return map;
}
/**
* @Description Shiro过滤器
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(){
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(defaultWebSecurityManager());
//使自定义过滤器生效
shiroFilter.setFilters(filters());
shiroFilter.setFilterChainDefinitionMap(filterChainDefinition());
shiroFilter.setLoginUrl("/login");
shiroFilter.setUnauthorizedUrl("/login");
return shiroFilter;
}
【2.2.3】编辑authentication.properties
#静态资源不过滤
/static/**=anon
#登录链接不过滤
/login/**=anon
#访问/resource/**需要有admin的角色
/resource/**=role-or[admin]
#其他链接是需要登录的
/**=authc
以下为常用注解
注解 | 说明 |
---|---|
@RequiresAuthentication | 表明当前用户需是经过认证的用户 |
@ RequiresGuest | 表明该用户需为”guest”用户 |
@RequiresPermissions | 当前用户需拥有指定权限 |
@RequiresRoles | 当前用户需拥有指定角色 |
@ RequiresUser | 当前用户需为已认证用户或已记住用户 |
例如RoleAction类中我们添加
/**
*@Description: 跳转到角色的初始化页面
*/
@RequiresRoles(value ={"SuperAdmin","dev"},logical = Logical.OR)
@RequestMapping(value = "listInitialize")
public ModelAndView listInitialize(){
return new ModelAndView("/role/role-listInitialize");
}