spring 整合shiro ,并实现动态url 配置
shiro 与Spring 结合
数据库实现,参考RBAC角色权限控制的实现
maven 导入相关包
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
- web.xml 配置
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- spring_shiro.xml
<!-- 自定义Realm -->
<bean id="myRealm" class="com.nzs.listener.MyShiroRealm"/>
<!-- 缓存管理 -->
<bean id="mycacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"/>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
<property name="cacheManager" ref="mycacheManager"></property>
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- Shiro过滤器 -->
<!--<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">-->
<bean id="shiroFilter" class="com.nzs.listener.ShiroPermissionFactory">
<property name="manageUserService" ref="manageUserService"/>
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份认证失败,则跳转到登录页面的配置 -->
<property name="loginUrl" value="/login.jsp"/>
<!-- 登录成功跳转页面的配置 -->
<property name="successUrl" value="/index"/>
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/norole.jsp"/>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
/manageUser/login=anon
<!--/** = authc-->
</value>
</property>
</bean>
- 自定义的realm
public class MyShiroRealm extends AuthorizingRealm {
static IManageUserService manageUserService;
public static final String SESSION_USER_KEY = "temp";
/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//可自定义实现内容
SecurityUtils.getSubject().getSession()
.getAttribute(OneballShiroRealm.SESSION_USER_KEY);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
/**
* 认证回调函数,登录信息和用户验证信息验证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
//判断 manageUserService 是否为空
if (manageUserService == null) {
manageUserService = (ManageUserService) ApplicationContextUtil.getBean("manageUserService");
}
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String userName = token.getUsername();
String passWord = String.valueOf(token.getPassword());
ManageUser manageUser = manageUserService.login(userName, passWord);
if (manageUser == null) {
return null; // 异常处理,找不到数据
}
Subject subject = SecurityUtils.getSubject();
//shiro session
Session session = SecurityUtils.getSubject().getSession();
session.setTimeout(60 * 60 * 3 * 1000);
session.setAttribute(OneballShiroRealm.SESSION_USER_KEY, manageUser);
//当前 Realm 的 name
String realmName = this.getName();
//登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
Object principal = authcToken.getPrincipal();
return new SimpleAuthenticationInfo(principal, passWord, realmName);
}
public IManageUserService getManageUserService() {
return manageUserService;
}
public void setManageUserService(IManageUserService manageUserService) {
this.manageUserService = manageUserService;
}
}
- 继承ShiroFilterFactoryBean,实现从数据库动态加载权限信息
public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
private ManageUserService manageUserService;
/**记录配置中的过滤链*/
public static String definition="";
/**
* 初始化设置过滤链
*/
@Override
public void setFilterChainDefinitions(String definitions) {
// String token = manageUserService.getAdminToken(0);
//可从数据库读取后,添加至过滤链,参考此处已注释的代码
definition = definitions;//记录配置的静态过滤链
// List<Permission> permissions = permissService.findAll();
Map<String, String> otherChains = new HashMap<String,String>();
// permissions.forEach(permiss->{
// //perms.add(e)
otherChains.put("/discover/newstag", "authc,roles[user,admin]");
// });
//加载配置默认的过滤链
Ini ini = new Ini();
ini.load(definitions);
Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
if (CollectionUtils.isEmpty(section)) {
section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
}
//加上数据库中过滤链
section.putAll(otherChains);
setFilterChainDefinitionMap(section);
}
public ManageUserService getManageUserService() {
return manageUserService;
}
public void setManageUserService(ManageUserService manageUserService) {
this.manageUserService = manageUserService;
}
}
- FilterChainDefinitionsService实现,无需部署,重新加载权限
@Service
public class FilterChainDefinitionsService implements IFilterChainDefinitionsService {
@Autowired
private ShiroPermissionFactory permissFactory;
@Override
public void reloadFilterChains() {
synchronized (permissFactory) { //强制同步,控制线程安全
AbstractShiroFilter shiroFilter = null;
try {
shiroFilter = (AbstractShiroFilter) permissFactory.getObject();
PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter
.getFilterChainResolver();
// 过滤管理器
DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();
// 清除权限配置
manager.getFilterChains().clear();
permissFactory.getFilterChainDefinitionMap().clear();
// 重新设置权限
permissFactory.setFilterChainDefinitions(ShiroPermissionFactory.definition);//传入配置中的filterchains
Map<String, String> chains = permissFactory.getFilterChainDefinitionMap();
//重新生成过滤链
if (!CollectionUtils.isEmpty(chains)) {
chains.forEach((url, definitionChains) -> {
manager.createChain(url, definitionChains.trim().replace(" ", ""));
});
}
// manager.addToChain("/discover/banner", "perms", "sssss");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 登录验证的实现
//示例代码,未实现加密验证
private String loginUser(ManageUser user) {
// if (isRelogin(user)) {
// // 如果已经登陆,无需重新登录
// return "success";
// }
return shiroLogin(user); // 调用shiro的登陆验证
}
private String shiroLogin(ManageUser user) {
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword().toCharArray(), null);
// token.setRememberMe(true);
// shiro登陆验证
try {
// System.out.println(SecurityUtils.getSubject());
SecurityUtils.getSubject().login(token);
} catch (UnknownAccountException ex) {
return "用户不存在或者密码错误!";
} catch (IncorrectCredentialsException ex) {
return "用户不存在或者密码错误!";
}catch (ExcessiveAttemptsException ex) {
return "账号或密码错误次数过多,请稍后重试!";
} catch (AuthenticationException ex) {
return ex.getMessage(); // 自定义报错信息
} catch (Exception ex) {
ex.printStackTrace();
return "内部错误,请重试!";
}
return "success";
}
private boolean isRelogin(ManageUser user) {
Subject us = SecurityUtils.getSubject();
if (us.isAuthenticated()) {
return true; // 参数未改变,无需重新登录,默认为已经登录成功
}
return false; // 需要重新登陆
}
声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。