文章目录
pom
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
shiroconfig
package pers.jaye.springboot.config;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzrenfeng.classbrand.service.impl.shiro.ShiroRealm;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
/**
*
* @Description _Shiro配置类
* @author 田杰熠
* @copyright {@link zzrenfeng.com}
* @version 2018年4月13日 上午10:43:50
* @see com.zzrenfeng.classbrand.config.ShiroConfig
*
*/
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
shiroFilterFactoryBean.setLoginUrl("/login"); // 未登录或者登录失败跳转路径
//自定义Filter
Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("authc", new LoginFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
// 拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
// 未授权url
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* securityManager 安全管理器
*
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSessionManager(sessionManager());
securityManager.setRealm(shiroRealm());
return securityManager;
}
/**
* 会话管理器 globalSessionTimeout session的失效时长(单位毫秒 ),deleteInvalidSessions
* 删除失效的session
*
* @return
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
defaultWebSessionManager.setGlobalSessionTimeout(1800000);
defaultWebSessionManager.setDeleteInvalidSessions(true); //删除无效sessions
defaultWebSessionManager.setSessionIdUrlRewritingEnabled(false); //禁止sessionId写入url
return defaultWebSessionManager;
}
/**
* 身份认证realm; 将自定义的Realm注入到securityManager 安全管理器中。
*
* @return
*/
@Bean
public ShiroRealm shiroRealm() {
ShiroRealm myShiroRealm = new ShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
/**
* 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了)
*
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5(""));
return hashedCredentialsMatcher;
}
/**
* LifecycleBeanPostProcessor将Initializable和Destroyable的实现类统一在其内部自动分别调用了Initializable
* .init() 和Destroyable.destroy()方法,从而达到管理shiro bean生命周期的目的。
*
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 开启_Shiro的注解(如@RequiresRoles,@RequiresPermissions),
* 需借助SpringAOP扫描使用_Shiro注解的类,并在必要时进行安全逻辑验证 配置以下两个bean
* DefaultAdvisorAutoProxyCreator(可选 - 但经过测试是必须配置的具体原因 暂时还不了解)
* AuthorizationAttributeSourceAdvisor 即可实现此功能
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
##shiroRealm
package pers.jaye.springboot.shiro;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.context.annotation.Lazy;
import com.zzrenfeng.classbrand.constant.Constant;
import com.zzrenfeng.classbrand.model.exception.ActivationAccountException;
import com.zzrenfeng.classbrand.model.shiro.ShiroUser;
import com.zzrenfeng.classbrand.model.sys.SysPermission;
import com.zzrenfeng.classbrand.model.sys.SysRole;
import com.zzrenfeng.classbrand.model.sys.SysUser;
import com.zzrenfeng.classbrand.service.sys.SysPermissionService;
import com.zzrenfeng.classbrand.service.sys.SysRoleService;
import com.zzrenfeng.classbrand.service.sys.SysUserService;
public class ShiroRealm extends AuthorizingRealm {
@Lazy
@Resource
private SysUserService sysUserService;
@Lazy
@Resource
private SysRoleService sysRoleService;
@Lazy
@Resource
private SysPermissionService sysPermissionService;
/**
* 身份认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userCode = (String) token.getPrincipal();
// 这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
SysUser user = sysUserService.findByUserCode(userCode);
if (user == null) {
return null;
}
Integer state = user.getState();
if (state.equals(Constant.USER_STATE_ACTIVATIONACCOUNT)) {
throw new ActivationAccountException(Constant.USER_LOGIN_EXCEPTION_ACTIVATIONACCOUNT);
} else if (state.equals(Constant.USER_STATE_DISABLEDACCOUNT)) {
throw new DisabledAccountException(Constant.USER_LOGIN_EXCEPTION_DISABLEDACCOUNT);
}
ShiroUser shiroUser = new ShiroUser();
shiroUser.setId(user.getId());
shiroUser.setNickname(user.getNickname());
shiroUser.setUserCode(user.getUserCode());
shiroUser.setPassword(user.getPassword());
shiroUser.setSalt(user.getSalt());
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(shiroUser, user.getPassword(), ByteSource.Util.bytes(user.getUserCode() + user.getSalt()), getName());
return authenticationInfo;
}
/**
* 权限配置
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
List<SysRole> roleList = sysRoleService.getRolesByUserId(shiroUser.getId());
List<SysPermission> permissionList = sysPermissionService.getPermissionsByUserId(shiroUser.getId());
Set<String> roles = roleList.stream().map(role -> role.getRole()).collect(Collectors.toSet());
Set<String> permissions = permissionList.stream().map(permission -> permission.getPermission())
.filter(string -> string != null).collect(Collectors.toSet());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
}
login
package com.zzrenfeng.classbrand.controller;
import java.util.Collection;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.zzrenfeng.classbrand.base.BaseController;
import com.zzrenfeng.classbrand.model.exception.ActivationAccountException;
import com.zzrenfeng.classbrand.service.impl.shiro.UserNamePasswordUserTypeToken;
import com.zzrenfeng.classbrand.service.sys.SysUserService;
import com.zzrenfeng.classbrand.util.Utils;
import com.zzrenfeng.classbrand.util.WriterUtils;
@Controller
public class LoginController extends BaseController {
@Resource
private Environment env;
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
@Resource
private SysUserService sysUserService;
/**
*/
@RequestMapping("/login")
public String login(HttpServletRequest request, Model model) throws Exception {
if (isLogined()) {
String jumpUrl = getJumpUrl(request);
return "redirect:" + jumpUrl;
}
String errorLoginMsg = getErrorLoginMsg(request);
model.addAttribute("msg", errorLoginMsg);
return "/login";
}
/**
* 登录失败从request中获取shiro处理的异常信息。 shiroLoginFailure:就是shiro异常类的全类名.
*
* @param request
* @return
*/
private String getErrorLoginMsg(HttpServletRequest request) {
String exception = (String) request.getAttribute("shiroLoginFailure");
String msg = "";
if (exception != null) {
if (UnknownAccountException.class.getName().equals(exception)) {
msg = "账号不存在";
} else if (IncorrectCredentialsException.class.getName().equals(exception)) {
msg = "密码不正确";
} else if (DisabledAccountException.class.getName().equals(exception)) {
msg = "账号已被禁用";
} else if (ActivationAccountException.class.getName().equals(exception)) {
msg = "账号尚未激活";
} else {
msg = exception;
}
}
return msg;
}
/**
* 获得跳转路径
*
* @param request
* @param subject
* @return
*/
private String getJumpUrl(HttpServletRequest request) {
String url = "";
SavedRequest savedRequest = WebUtils.getSavedRequest(request);
if (savedRequest != null) {
url = savedRequest.getRequestUrl();
String ctx = request.getContextPath();
url = url.substring(ctx.length());
}
return url;
}
}