项目基本配置参考SpringBoot入门一,使用myEclipse新建一个SpringBoot项目,使用myEclipse新建一个SpringBoot项目即可。现在来给项目添加shiro支持,数据暂时硬编码,不连接数据库,具体内容如下:
1. pom.xml添加以下配置信息
<!-- 开启shiro依赖 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
2. 创建shiro配置文件
2.1 自定义权限过滤器
package com.qfx.demo.shiro;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
import com.qfx.demo.vo.SysUser;
/**
* @功能描述: 拦截控制,用于替换shiro默认的roles拦截规则,改"并且(and)"为"或者 (or)"
*/
public class CustomRolesAuthorizationFilter extends RolesAuthorizationFilter {
private final Logger logger = LogManager.getLogger(getClass());
/**
* Overriding
* @功能描述:设置同一个URL配置多个角色为"或者"的关系,默认为"并且",
* 如:/user/** = Role["admin,user"],默认必须满足"admin","user"条件,
* 改为"或者"之后只需要满足一个条件即可(Ini.Section中有此url,会走此方法)
* @param request
* @param response
* @param obj
* @return
* @throws Exception
*/
@Override
public boolean isAccessAllowed(ServletRequest request,ServletResponse response, Object obj) {
Subject subject = getSubject(request, response);
// 验证是否登录
if (null == subject.getPrincipals()) {
return false;
}
// 获取请求地址
HttpServletRequest hsq = (HttpServletRequest) request;
String requestUrl = hsq.getServletPath();
// 获取用户信息,这里返回的对象类型与登录验证时
// new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一个参数的类型需要保持一致
SysUser user = (SysUser)subject.getPrincipals().getPrimaryPrincipal();
System.out.println("--------1.开启用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤--------");
// 获取角色信息
String[] rolesArray = (String[]) obj;
if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]无角色限制,权限验证通过!");
return true;
}
for (int i = 0; i < rolesArray.length; i++) {
if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]权限验证通过!");
return true;
}
}
System.out.println("--------3.用户["+user.getUserName()+"]访问["+requestUrl+"]的角色过滤结束--------");
logger.info("用户["+user.getUserName()+"]访问["+requestUrl+"]权限验证失败,禁止访问!");
return false;
}
}
2.2 自定义realm
package com.qfx.demo.shiro;
import java.util.HashSet;
import java.util.Set;
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.UnknownAccountException;
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 com.qfx.demo.cache.UserCache;
import com.qfx.demo.vo.SysUser;
public class UserRealm extends AuthorizingRealm {
/**
* Overriding
* @功能描述: 权限验证
* @param arg0
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 1.获取用户信息
// 这里principals.getPrimaryPrincipal()的返回的对象类型与登录验证时
// new SimpleAuthenticationInfo(user, pwd, this.getName())中的第一个参数的类型需要保持一致
SysUser user = (SysUser)principals.getPrimaryPrincipal();
System.out.println("--------2.用户["+user.getUserName()+"]进行权限验证--------");
// 2.单独定一个集合对象放置角色信息
Set<String> roles = new HashSet<String>();
roles.add(user.getRoleName());
// 3.查到权限数据,返回授权信息(要包括 上边的permissions)
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
return simpleAuthorizationInfo;
}
/**
* Overriding
* @功能描述: 登陆验证
* @param arg0
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
System.out.println("---------用户登录验证---------");
// 1.获取登录名称
String userName = token.getPrincipal().toString();
// 2.根据登录名称获取用户信息(从缓存获取,正式项目从数据库)
SysUser user = UserCache.getUserCacheMap(userName);
if (null == user) {
// 抛出账户不存在的异常
throw new UnknownAccountException();
}
// 3.获取查询到到密码和盐值
String pwd = user.getPassWord();
String salt = user.getSalt();
// 4.交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
// new SimpleAuthenticationInfo(user, pwd, this.getName())中的user是SysUser对象,在其他接收的地方也要转成SysUser对象
SimpleAuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, pwd, this.getName());
// 设置盐值(salt = pwd+userName + salt)
authcInfo.setCredentialsSalt(ByteSource.Util.bytes(pwd+userName + salt));
return authcInfo;
}
}
2.3 自定义shiro核心配置类(取代xml配置文件)
package com.qfx.demo.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.qfx.demo.cache.MenuRoleCache;
import com.qfx.demo.vo.SysMenuRole;
@Configuration
public class ShiroConfig {
/**
* <h5>功能:凭证匹配器</h5>
* @return
*/
@Bean
public HashedCredentialsMatcher hashMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//采用md5加密,没有默认值.可以有MD5或者SHA-1,如果对密码安全有更高要求可以用SHA-256或者更高
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//散列的次数,比如散列2次,相当于md5(md5(""));
hashedCredentialsMatcher.setHashIterations(2);
//默认是true,true时用的是Hex编码;false时用Base64编码
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
return hashedCredentialsMatcher;
}
/**
* <h5>功能:配置shiro session 的一个管理器</h5>
* @return
*/
@Bean
public DefaultWebSessionManager sessionManager(){
DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
defaultWebSessionManager.setSessionDAO(new MemorySessionDAO());
// 隐藏在地址栏中显示的JSESSIONID
defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false);
// session的失效时长,单位毫秒(这里设置为30分钟,实际项目请自行修改)
defaultWebSessionManager.setGlobalSessionTimeout(1800000);
// 间隔一定时间清理失效会话,单位毫秒(这里设置为每5分钟清理一次用户直接关闭浏览器造成的孤立会话,实际项目请自行修改)
defaultWebSessionManager.setSessionValidationInterval(300000);
// 描session线程,负责清理超时会话
defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
return defaultWebSessionManager;
}
/**
* <h5>功能:自定义realm认证类,继承自AuthorizingRealm,负责用户的认证和权限的处理</h5>
* @param hashMatcher
* @return
*/
@Bean
public UserRealm userRealm(HashedCredentialsMatcher hashMatcher){
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(hashMatcher);
return userRealm;
}
/**
* <h5>功能:安全管理器</h5>
* 权限管理,这个类组合了登陆,登出,权限,session的处理,是个比较重要的类
* @param userRealm
* @param sessionManager
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(UserRealm userRealm, DefaultWebSessionManager sessionManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 自定义realm
securityManager.setRealm(userRealm);
// shiro 会话管理
securityManager.setSessionManager(sessionManager);
return securityManager;
}
/**
* <h5>功能:自定义权限过滤器</h5>
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 调用我们配置的安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 配置我们的登录请求地址,非必须的属性,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
shiroFilterFactoryBean.setLoginUrl("/view/error/401.jsp");
// 设置无权限时跳转的URL
shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.jsp");
Map<String, Filter> filter = shiroFilterFactoryBean.getFilters();
filter.put("roles", new CustomRolesAuthorizationFilter());
// 设置拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
shiroFilterFactoryBean.setFilters(filter);
// 对静态资源设置匿名访问,从resoutces/static后面开始写
filterChainDefinitionMap.put("/css/**", "anon");
// 可匿名访问的地址
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/index.jsp", "anon");
filterChainDefinitionMap.put("/login/loginPage", "anon");
filterChainDefinitionMap.put("/login/register", "anon");
filterChainDefinitionMap.put("/login/login", "anon");
// 请求 logout.do地址,shiro去清除session
filterChainDefinitionMap.put("/logout", "logout");
//循环url,逐个添加到section中。section就是filterChainDefinitionMap,
//里面的键就是链接URL,值就是存在什么条件才能访问该链接
Map<String, SysMenuRole> menuRoleMap = MenuRoleCache.menuRoleCacheMap;
for (String key : menuRoleMap.keySet()) {
filterChainDefinitionMap.put(key, "roles["+menuRoleMap.get(key).getRoleNames()+"]");
}
//所有url都必须认证通过才可以访问,必须放在最后
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
3.其他文件 javaBean文件和缓存类等信息就不从这里贴出来了,核心的就是上面的几个文件,想要应用的话,只要把对应的获取数据的地方修改成自己的实现就可以了。