ShiroConfig.java
import com.hckj.iot.core.interceptor.UserFilter;
import com.hckj.iot.core.shiro.ShiroDbRealm;
import com.hckj.iot.core.shiro.matcher.MyRetryLimitCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.RememberMeManager;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.servlet.Filter;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/**
* web应用管理配置
* @param matcher
* @param cacheManager
* @param manager
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(MyRetryLimitCredentialsMatcher matcher, CacheManager cacheManager, RememberMeManager manager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(cacheManager);
securityManager.setRememberMeManager(manager);//记住Cookie
securityManager.setSessionManager(sessionManager());
securityManager.setRealm(shiroDbRealm(matcher));
return securityManager;
}
/**
* 加密算法
* 密码匹配凭证管理器
* @return
*/
@Bean(name = "myRetryLimitCredentialsMatcher")
public MyRetryLimitCredentialsMatcher hashedCredentialsMatcher() {
MyRetryLimitCredentialsMatcher hashedCredentialsMatcher = new MyRetryLimitCredentialsMatcher();
// 采用MD5方式加密
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// 设置加密次数
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
/**
* spring session管理器(多机环境)
*/
@Bean
@ConditionalOnProperty(prefix = "web", name = "spring-session-open", havingValue = "true")
public ServletContainerSessionManager servletContainerSessionManager() {
return new ServletContainerSessionManager();
}
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager();
Long timeout=60L*1000*60;//毫秒级别 设置session过期时间3600s
defaultWebSessionManager.setGlobalSessionTimeout(timeout);
return defaultWebSessionManager;
}
/**
* 缓存管理器 使用Ehcache实现
*/
@Bean
public CacheManager getCacheShiroManager(EhCacheManagerFactoryBean ehcache) {
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManager(ehcache.getObject());
return ehCacheManager;
}
/**
* 项目自定义的Realm
*/
@Bean
public ShiroDbRealm shiroDbRealm(MyRetryLimitCredentialsMatcher matcher) {
ShiroDbRealm myShiroRealm = new ShiroDbRealm();
myShiroRealm.setCredentialsMatcher(matcher);
return myShiroRealm;
}
/**
* rememberMe管理器, cipherKey生成见{@code Base64Test.java}
*/
@Bean
public CookieRememberMeManager rememberMeManager(SimpleCookie rememberMeCookie) {
CookieRememberMeManager manager = new CookieRememberMeManager();
manager.setCipherKey(generateNewKey());
// manager.setCipherKey(Base64.decode("Z3VucwAAAAAAAAAAAAAAAA=="));
manager.setCookie(rememberMeCookie);
return manager;
}
/**
* 自动生成 remember key
* @return
*/
private static byte[] generateNewKey() {
KeyGenerator kg;
try {
kg = KeyGenerator.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
String msg="this is required to function!";
throw new IllegalStateException(msg, e);
}
kg.init(128);
SecretKey key = kg.generateKey();
byte[] encoded = key.getEncoded();
return encoded;
}
/**
* 记住密码Cookie
*/
@Bean
public SimpleCookie rememberMeCookie() {
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setHttpOnly(true);
simpleCookie.setMaxAge(7 * 24 * 60 * 60);//7天
return simpleCookie;
}
/**
* Shiro的过滤器链
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
/**
* 默认的登陆访问url
*/
shiroFilter.setLoginUrl("/login");
/**
* 登陆成功后跳转的url
*/
shiroFilter.setSuccessUrl("/");
/**
* 没有权限跳转的url
*/
shiroFilter.setUnauthorizedUrl("/global/error");
/**
* 覆盖默认的user拦截器(默认拦截器解决不了ajax请求 session超时的问题,若有更好的办法请及时反馈作者)
*/
HashMap<String, Filter> myFilters = new HashMap<>();
myFilters.put("user", new UserFilter());
shiroFilter.setFilters(myFilters);
/**
* 配置shiro拦截器链
*
* anon 不需要认证
* authc 需要认证
* user 验证通过或RememberMe登录的都可以
*
* 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
*
* 顺序从上到下,优先级依次降低
*
* api开头的接口,走rest api鉴权,不走shiro鉴权
*
*/
Map<String, String> hashMap = new LinkedHashMap<>();
hashMap.put("/static/**", "anon");
hashMap.put("/api/**", "anon");
hashMap.put("/login", "anon");
hashMap.put("/inner", "anon");
hashMap.put("/innerForProjectId", "anon");
hashMap.put("/global/sessionError", "anon");
hashMap.put("/kaptcha", "anon");
hashMap.put("/**", "user");
shiroFilter.setFilterChainDefinitionMap(hashMap);
return shiroFilter;
}
/**
* 在方法中 注入 securityManager,进行代理控制
*/
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager securityManager) {
MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
bean.setArguments(new Object[]{securityManager});
return bean;
}
/**
* Shiro生命周期处理器:
* 用于在实现了Initializable接口的Shiro bean初始化时调用Initializable接口回调(例如:UserRealm)
* 在实现了Destroyable接口的Shiro bean销毁时调用 Destroyable接口回调(例如:DefaultSecurityManager)
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 启用shrio授权注解拦截方式,AOP式方法级权限检查
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
MyRetryLimitCredentialsMatcher.java
import com.hckj.iot.core.shiro.oauth.CustomeToken;
import com.hckj.iot.core.shiro.oauth.LoginType;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyRetryLimitCredentialsMatcher extends HashedCredentialsMatcher {
public MyRetryLimitCredentialsMatcher() {
}
public MyRetryLimitCredentialsMatcher(String hashAlgorithmName) {
super(hashAlgorithmName);
}
@Override
public boolean doCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo info) {
if (authcToken instanceof CustomeToken) {
CustomeToken customeToken = (CustomeToken) authcToken;
if(customeToken.getType().equals(LoginType.NOPASSWD)){
return true;
}
}
//不是免密登录,调用父类的方法
return super.doCredentialsMatch(authcToken, info);
}
}
CustomeToken.java
import lombok.Data;
import org.apache.shiro.authc.UsernamePasswordToken;
@Data
public class CustomeToken extends UsernamePasswordToken {
private static final long serialVersionUID = -2564928913725078138L;
private LoginType type;
public CustomeToken() {
super();
}
public CustomeToken(String username, String password, LoginType type, boolean rememberMe, String host) {
super(username, password, rememberMe, host);
this.type = type;
}
/**
* 免密登录
*/
public CustomeToken(String username) {
super(username, "", false, null);
this.type = LoginType.NOPASSWD;
}
/**
* 账号密码登录
*/
public CustomeToken(String username, String password) {
super(username, password, false, null);
this.type = LoginType.PASSWORD;
}
public LoginType getType() {
return type;
}
public void setType(LoginType type) {
this.type = type;
}
}
LoginType.java
public enum LoginType {
PASSWORD("password"), // 密码登录
NOPASSWD("nopassword"); // 免密登录
private String code;// 状态值
private LoginType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
ShiroDbRealm.java
import com.hckj.iot.core.shiro.service.UserAuthService;
import com.hckj.iot.core.shiro.service.impl.UserAuthServiceServiceImpl;
import com.hckj.iot.modular.system.entity.User;
import com.hckj.iot.core.util.ToolUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
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 java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ShiroDbRealm extends AuthorizingRealm {
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken)
throws AuthenticationException {
UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
User user = shiroFactory.user(token.getUsername());
ShiroUser shiroUser = shiroFactory.shiroUser(user);
return shiroFactory.info(shiroUser, user, super.getName());
}
/**
* 权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
List<Integer> roleList = shiroUser.getRoleList();
Set<String> permissionSet = new HashSet<>();
Set<String> roleNameSet = new HashSet<>();
for (Integer roleId : roleList) {
List<String> permissions = shiroFactory.findPermissionsByRoleId(roleId);
if (permissions != null) {
for (String permission : permissions) {
if (ToolUtil.isNotEmpty(permission)) {
permissionSet.add(permission);
}
}
}
String roleName = shiroFactory.findRoleNameByRoleId(roleId);
roleNameSet.add(roleName);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionSet);
info.addRoles(roleNameSet);
return info;
}
/**
* 设置认证加密方式
*/
@Override
public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
super.setCredentialsMatcher(credentialsMatcher);
}
}
LoginController.java
Subject currentUser = ShiroKit.getSubject();
UsernamePasswordToken token = new CustomeToken(account);
currentUser.login(token);