1 配置文件
import com.chaoqi.springboot_shiro_redis.secutity.KickoutSessionControlFilter;
import com.chaoqi.springboot_shiro_redis.secutity.MyShiroRealm;
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.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 没有登陆的用户只能访问登陆页面
shiroFilterFactoryBean.setLoginUrl("/auth/login");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/auth/index");
// 未授权界面; ----这个配置了没卵用,具体原因想深入了解的可以自行百度
//shiroFilterFactoryBean.setUnauthorizedUrl("/auth/403");
//自定义拦截器
Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
//限制同一帐号同时在线的个数。
filtersMap.put("kickout", kickoutSessionControlFilter());
shiroFilterFactoryBean.setFilters(filtersMap);
// 权限控制map.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/auth/login", "anon");
filterChainDefinitionMap.put("/auth/logout", "logout");
filterChainDefinitionMap.put("/auth/kickout", "anon");
filterChainDefinitionMap.put("/**", "authc,kickout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
// 自定义缓存实现 使用redis
securityManager.setCacheManager(cacheManager());
// 自定义session管理 使用redis
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* 身份认证realm; (这个需要自己写,账号密码校验;权限等)
*
* @return
*/
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
/**
* cacheManager 缓存 redis实现
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* 配置shiro redisManager
* 使用的是shiro-redis开源插件
*
* @return
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost("localhost");
redisManager.setPort(6379);
redisManager.setExpire(1800);// 配置缓存过期时间
redisManager.setTimeout(0);
// redisManager.setPassword(password);
return redisManager;
}
/**
* Session Manager
* 使用的是shiro-redis开源插件
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
* 使用的是shiro-redis开源插件
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* 限制同一账号登录同时登录人数控制
*
* @return
*/
@Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManager());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");
return kickoutSessionControlFilter;
}
/***
* 授权所用配置
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
/***
* 使授权注解起作用不如不想配置可以在pom文件中加入
* <dependency>
*<groupId>org.springframework.boot</groupId>
*<artifactId>spring-boot-starter-aop</artifactId>
*</dependency>
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* Shiro生命周期处理器
*
*/
@Bean
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
二 自己写的身份认证授权MyShiroRealm
1 需要自己写身份认证,调用自己的业务层,根据用户提交的用户名和密码在数据库中进行查找,并对密码进行对比,如果相等则登录,如果数据库中的密码为空则代表没有改用户,如果密码存在但是不相等则表示密码错误,抛出异常:代码如下:
//获取账号密码
UsernamePasswordToken t = (UsernamePasswordToken) token;
String userName= token.getPrincipal().toString();
String password= new String( t.getPassword());
//获取数据库中的密码
String passwordInDB = new DAO().getPassword(userName);
//如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不是抛出具体错误原因,免得给破解者提供帮助信息
if(null==passwordInDB || !passwordInDB.equals(password))
throw new AuthenticationException();
/**
* 认证信息.(身份验证) : Authentication 是用来验证用户身份
*
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
logger.info("---------------- 执行 Shiro 凭证认证 ----------------------");
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
String name = token.getUsername();
String password = String.valueOf(token.getPassword());
SysUser user = new SysUser();
user.setUserName(name);
user.setPassWord(password);
// 从数据库获取对应用户名密码的用户
//根据自己的业务逻辑查询用户,具体情况需要做修改
SysUser userList = userService.getUser(user);
if (userList != null) {
// 用户为禁用状态
if (userList.getUserEnable() != 1) {
throw new DisabledAccountException();
}
logger.info("---------------- Shiro 凭证认证成功 ----------------------");
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
//用户
userList,
//密码
userList.getPassWord(),
//realm name
getName()
);
return authenticationInfo;
}
throw new UnknownAccountException();
}
2 需要自己写授权逻辑,调用自己得业务逻辑进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
logger.info("---------------- 执行 Shiro 权限获取 ---------------------");
Object principal = principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
if (principal instanceof SysUser) {
SysUser userLogin = (SysUser) principal;
//根据自己的业务逻辑查询角色,需要做修改
Set<String> roles = roleService.findRoleNameByUserId(userLogin.getId());
authorizationInfo.addRoles(roles);
//根据自己的业务逻辑进行修改,查询该用户所拥有的
Set<String> permissions = userService.findPermissionsByUserId(userLogin.getId());
authorizationInfo.addStringPermissions(permissions);
}
logger.info("---- 获取到以下权限 ----");
logger.info(authorizationInfo.getStringPermissions().toString());
logger.info("---------------- Shiro 权限获取成功 ----------------------");
return authorizationInfo;
}
三 用户登录的接口层写法
import com.chaoqi.springboot_shiro_redis.dao.domain.SysUser;
import com.chaoqi.springboot_shiro_redis.utils.RequestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping(value = "/auth")
public class LoginController {
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String submitLogin(String username, String password, HttpServletRequest request) {
try {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
subject.login(token);
SysUser user = (SysUser) subject.getPrincipal();
} catch (DisabledAccountException e) {
request.setAttribute("msg", "账户已被禁用");
return "login";
} catch (AuthenticationException e) {
request.setAttribute("msg", "用户名或密码错误");
return "login";
}
// 执行到这里说明用户已登录成功
return "redirect:/auth/index";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginPage() {
return "login";
}
@RequestMapping(value = "/index", method = RequestMethod.GET)
public String loginSuccessMessage(HttpServletRequest request) {
String username = "未登录";
SysUser currentLoginUser = RequestUtils.currentLoginUser();
if (currentLoginUser != null && StringUtils.isNotEmpty(currentLoginUser.getUserName())) {
username = currentLoginUser.getUserName();
} else {
return "redirect:/auth/login";
}
request.setAttribute("username", username);
return "index";
}
//被踢出后跳转的页面
@RequestMapping(value = "/kickout", method = RequestMethod.GET)
public String kickOut() {
return "kickout";
}
}
四 获取当前登录的用户,若未登录,则返回未登录的json
import com.chaoqi.springboot_shiro_redis.dao.domain.SysUser;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestUtils {
private static Logger logger = LoggerFactory.getLogger(RequestUtils.class);
/**
* 获取当前登录的用户,若用户未登录,则返回未登录 json
*
* @return
*/
public static SysUser currentLoginUser() {
Subject subject = SecurityUtils.getSubject();
if (subject.isAuthenticated()) {
Object principal = subject.getPrincipals().getPrimaryPrincipal();
if (principal instanceof SysUser) {
return (SysUser) principal;
}
}
return null;
}
}