之前一直对spring-boot没有配置文件的说法很好奇, 一直想怎么可能呢, 一个项目那么大,集成的技术框架那么多, 没有配置文件spring-boot它怎么知道该怎么做,难道他是神一样的存在,
通过这几天对spring-boot的研究发现, 之前说spring-boot 零配置 消灭配置文件的说法, 就感觉是换汤不换药, 新瓶装旧酒一样
只是把xml的配置文件 换成了用java类+注解的方式来实现了, 自已感觉这种方式还倒退回去了, 用起来很麻烦, 没有单独使用spring那么好用,灵活, 个人还是喜欢使用spring + mvc
下面将本人将Spring-boot2整合Shiro 并开启Shiro权限标签的代码帖一下, 留个记念
1.加入依赖
org.apache.shiro
shiro-web
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
org.apache.shiro
shiro-core
${shiro.version}
slf4j-api
org.slf4j
2.自定义realm 实现 org.apache.shiro.realm.AuthorizingRealm 进行身份跟权限验证
package com.dance.admin.security;
import com.dance.common.util.StringUtil;
import com.dance.enums.ShiroRoleEnum;
import com.dance.model.ShiroPerm;
import com.dance.model.ShiroUser;
import com.dance.pojo.ShiroUserPO;
import com.dance.service.ShiroPermService;
import com.dance.service.ShiroUserService;
import com.dance.util.SessionKit;
import com.dance.util.SessionShiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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 org.springframework.stereotype.Component;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
/**
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
*
* @developers LONGZHIQIANG
* @createtime 2019-07-01 11:47.
* @describe shiro安全认证自定义规则
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
*/
@Slf4j
@Component
public class ShiroDbRealm extends AuthorizingRealm {
//页面在session中获取用户菜单时的key
public static String PERMISSIONS_KEY = "permissions";
@Autowired
private ShiroUserService shiroUserService;
@Autowired
private ShiroPermService shiroPermService;
/**
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
* 采用构造方法初使化设定 realm的相关参数
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
*/
ShiroDbRealm(){
this("MD5",1024);
this.setCachingEnabled(true);
this.setAuthenticationCachingEnabled(false);
this.setAuthorizationCachingEnabled(true);
}
ShiroDbRealm(String hashAlgorithmName, Integer hashIterations){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName(hashAlgorithmName);
matcher.setHashIterations(hashIterations);
this.setCredentialsMatcher(matcher);
}
/**
* 认证回调函数,登录时调用.
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
//1、根据用户名查询用户
ShiroUserPO user = userGetByName(token.getUsername());
//2、当用户角色为超级管理员则获取系统所有菜单(该角色所属权限数据库没有映射数据,默认拥有为所有权限)
if (user.getRoleIdSet().contains(ShiroRoleEnum.SUPERADMIN.getId().toString())) {
List perms = shiroPermService.selectAll();
user.setPerms(new HashSet<>(perms));
}
//3、将系统后台菜单存入用户sessiom
List permList = user.getPerms().stream().sorted(Comparator.comparing(ShiroPerm::getPermCode)).collect(Collectors.toList());
SessionKit.setAttribute(PERMISSIONS_KEY, permList);
ByteSource salt = ByteSource.Util.bytes(user.getSalt());
return new SimpleAuthenticationInfo(new SessionShiro(user), user.getPin(), salt, getName());
}
/**
* 授权查询回调函数, 进行鉴权同时缓存中无用户的授权信息时调用.
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SessionShiro user = (SessionShiro) principals.getPrimaryPrincipal();
if (StringUtil.isEmpty(user)) { return null; }
SimpleAuthorizationInfo simpleInfo = new SimpleAuthorizationInfo();
ShiroUserPO po = userGetByName(user.name);
simpleInfo.addRoles(po.getRoleIdSet());
if (po.getRoleIdSet().contains(ShiroRoleEnum.SUPERADMIN.getId().toString())) {
List all = shiroPermService.selectAll();
List permissions = all.stream().map(ShiroPerm::getPermPermission).collect(Collectors.toList());
simpleInfo.addStringPermissions(permissions);
} else {
simpleInfo.addStringPermissions(po.getPermIdSet());
}
return simpleInfo;
}
//根据用户名根据用户相关信息
private ShiroUserPO userGetByName(String name) {
List pos = shiroUserService.queryLinkPrem(new ShiroUser().setName(name));
if (StringUtil.isEmpty(pos)) {
throw new IncorrectCredentialsException();
}
if (pos.size() > 1) {
log.error("用户数据异常,帐号 {} 查询到多个相同帐户", name);
throw new IncorrectCredentialsException();
}
return pos.get(0);
}
}
3.定义配置类 这个配置文件以前就是一个 application-shiro.xml, 只是用java代码的方法去实现而以 新瓶装旧酒
package com.dance.admin.security;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
/**
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
*
* @developers LONGZHIQIANG
* @createtime 2019-07-01 10:26.
* @describe shiro 全局配置类
* ☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆☆☆★☆☆☆☆
*/
@Configuration
public class ShiroDbConfig {
/**
* 初使化安全管理器
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager initDefaultWebSecurityManager(@Qualifier("shiroDbRealm") ShiroDbRealm shiroDbRealm){
DefaultWebSecurityManager security = new DefaultWebSecurityManager();
security.setRealm(shiroDbRealm);
return security;
}
/**
* 初使化shiroFilter处理路径相关设置
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//身份认证失败[未登入],则跳转到登录页面 在controller定这个路径直接转发到首页
filter.setLoginUrl("/");
filter.setSecurityManager(securityManager);
filter.setFilterChainDefinitionMap(new LinkedHashMap(){
{
put("/logout", "logout");
put("/logout", "logout");
put("/IOCloud/**", "authc");
put("/admin/**", "authc");
put("/**", "anon");
}
});
return filter;
}
/***
* 保证实现了Shiro内部lifecycle函数的bean执行
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启Shiro的注解java注解,
* 需借助SpringAOP扫描使用Shiro注解的类
* 并在必要时进行安全逻辑验证
*/
@Bean(name = "authorizationAttributeSourceAdvisor")
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
sourceAdvisor.setSecurityManager(securityManager);
return sourceAdvisor;
}
/**
* 解决与SiteMesh与shiro filter冲突导致 jsp页面 shiro标签无效问题
* SiteMeshFilter在处理时,调用了context.decorate(decoratorPath, content),
* 这导致了ApplicationDispatcher.forward操作
* forward操作里,又重新构建filter chain:
* Shiro Filter,刚好排在SiteMeshFilter之后,
* 于是在Forward之前和之后都没有执行。
* 该解决方案来源于该播客 https://blog.csdn.net/elashu/article/details/85089891
*/
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*");
//filterRegistrationBean.setAsyncSupported(true);
EnumSet types = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
filterRegistrationBean.setDispatcherTypes(types);
return filterRegistrationBean;
}
}
4.搞定收工