ShiroConfig 配置类:
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;import com.ck.syscheck.shiro.ShiroRealm;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.codec.Base64;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.CookieRememberMeManager;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.apache.shiro.web.servlet.SimpleCookie;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;import java.util.Map;@Configurationpublic class ShiroConfig {private Logger logger = LoggerFactory.getLogger(this.getClass());/*** 创建ShiroFilterFactoryBean** @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 设置安全管理器shiroFilterFactoryBean.setSecurityManager(securityManager);//添加Shiro内置过滤器/*** 常用过滤器* anon:无需认证(登录)就可以访问* authc:必须认证才可以访问* user:如果使用RememberMe的功能,可以直接访问* perms:该资源必须得到资源权限才可以访问* role:该资源必须得到角色权限才可以访问*///登录的urlshiroFilterFactoryBean.setLoginUrl("/api/loginPage");// 未授权urlshiroFilterFactoryBean.setUnauthorizedUrl("/api/unAuth");// 登录成功后跳转的urlshiroFilterFactoryBean.setSuccessUrl("/index");//注意此处要使用LinkenHashMapMap<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/", "authc");// 定义filterChain,静态资源不拦截filterMap.put("/css/**", "anon");filterMap.put("/js/**", "anon");filterMap.put("/fonts/**", "anon");filterMap.put("/img/**", "anon");//放行某些个请求filterMap.put("/api/test", "anon");filterMap.put("/api/login", "anon");//授权过滤器,如果未授权会跳转到相应的页面filterMap.put("/api/add", "perms[api:add]");// 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl// filterMap.put("/api/*", "authc");// filterMap.put("/home/*","authc");// filterMap.put("/config/*","authc");// 配置退出过滤器,其中具体的退出代码 Shiro已经替我们实现了filterMap.put("/logout", "logout");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);return shiroFilterFactoryBean;}/**** 创建DefaultWebSecurityManager* @param matcher* @return*/@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//关联RealmsecurityManager.setRealm(shiroRealm(matcher));//记住我securityManager.setRememberMeManager(rememberMeManager());//缓存管理securityManager.setCacheManager(getEhCacheManager());return securityManager;}/*** 创建Relm** @return*/@Beanpublic ShiroRealm shiroRealm(HashedCredentialsMatcher matcher) {ShiroRealm myShiroRealm = new ShiroRealm();myShiroRealm.setCredentialsMatcher(matcher);return myShiroRealm;}/*** 配置ShiroDialect,用于整合shiro和Thymeleaf标签配合使用* @return*/@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}/*** 密码匹配凭证管理器** @return*/@Bean(name = "hashedCredentialsMatcher")public HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 采用MD5方式加密hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 设置加密次数hashedCredentialsMatcher.setHashIterations(1024);return hashedCredentialsMatcher;}/*** cookie对象* @return*/public SimpleCookie rememberMeCookie() {// 设置cookie名称,对应login.html页面的<input type="checkbox" name="rememberMe"/>SimpleCookie cookie = new SimpleCookie("rememberMe");// 设置cookie的过期时间,单位为秒,这里为一天cookie.setMaxAge(86400);return cookie;}/*** cookie管理对象* @return*/public CookieRememberMeManager rememberMeManager() {CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());// rememberMe cookie加密的密钥cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));return cookieRememberMeManager;}/*** 注入Ehcache缓存* @return*/@Beanpublic EhCacheManager getEhCacheManager() {EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");return ehCacheManager;}}
此类主要使用 hashedCredentialsMatcher 其加盐处理:
/*** 密码匹配凭证管理器** @return*/@Bean(name = "hashedCredentialsMatcher")public HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();// 采用MD5方式加密hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 设置加密次数hashedCredentialsMatcher.setHashIterations(1024);return hashedCredentialsMatcher;}
ShiroRealm 类:
import com.ck.syscheck.model.*;import com.ck.syscheck.service.SysResourceService;import com.ck.syscheck.service.SysRoleResourceService;import com.ck.syscheck.service.SysUserRoleService;import com.ck.syscheck.service.SysUserService;import org.apache.commons.lang3.StringUtils;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.*;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.subject.Subject;import org.apache.shiro.util.ByteSource;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.servlet.ModelAndView;import javax.annotation.Resource;import java.util.ArrayList;import java.util.List;public class ShiroRealm extends AuthorizingRealm {private Logger logger = LoggerFactory.getLogger(this.getClass());@Resourceprivate SysUserService sysUserService;@Resourceprivate SysUserRoleService sysUserRoleService;/*** 处理授权逻辑** @param principalCollection* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {logger.info("执行授权逻辑+" + principalCollection.toString());//给资源进行授权SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();Subject subject = SecurityUtils.getSubject();SysUser user = (SysUser) subject.getPrincipal();//获取用户角色IDSysUserRole byUserId = sysUserRoleService.findByUserId(user.getUid());//此处必须要和配置文件里的授权保持一致authorizationInfo.addStringPermission("api:add");return authorizationInfo;}/*** 处理认证逻辑** @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;// 获取用户输入的用户名和密码String userName = (String) token.getPrincipal();String password = new String((char[]) token.getCredentials());if (StringUtils.isNotBlank(userName)) {SysUser user = sysUserService.list(userName);//检测是否有此用户if (user == null) {// 没有找到账号异常throw new UnknownAccountException("用户名或密码错误!");}if (!token.getUsername().equals(user.getUsername())) {return null;}if (user.getStatus().equals("0")) {throw new LockedAccountException("账号已被锁定,请联系管理员!");}// 获取盐值,即用户名ByteSource salt = ByteSource.Util.bytes("AaBbCc");// 将账户名,密码,盐值,realmName实例化到SimpleAuthenticationInfo中交给Shiro来管理SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), salt, getName());//返回密码return simpleAuthenticationInfo;}return null;}}
在Realm类中主要传递加盐的值:
// 获取盐值,即用户名,此处盐值可以是活的,可以是用户输入的用户名等,也可以是写死的ByteSource salt = ByteSource.Util.bytes("AaBbCc");// 将账户名,密码,盐值,realmName实例化到SimpleAuthenticationInfo中交给Shiro来管理SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), salt, getName());//返回密码return simpleAuthenticationInfo;
测试Shiro加盐,一般在注册的时候进行如下处理,最后保存到数据库。
import org.apache.shiro.crypto.hash.SimpleHash;import org.apache.shiro.util.ByteSource;/*** 测试Shiro加盐,一般在注册的时候进行如下处理,最后保存到数据库* @author ck* @date 2019/7/8*/public class Test {public static void main(String[] args) {String newPs = new SimpleHash("MD5", "123456", ByteSource.Util.bytes("AaBbCc"), 1024).toHex();System.out.println(newPs);//6a630026f54e3af745becaa18dd1ca6f}}
