Shiro:能够执行身份验证,授权,加密,会话管理(全局session)
shiro:操作中对象与方法
-
Subject : 当前操作用户,即当前与shiro进行交互的应用
-
SecurityManager:Shiro通过该方法来管理内部组件实例,并提供安全管理的各种服务
-
Realm:安全数据源(认证和授权数据),配置Shiro时,至少指定一个Realm,用户认证授权(Shiro内置了可连接大量安全数据源的Realm,若默认Realm不满足需求,则可以自动以Realm)
SpringBoot 集成 Shiro
引入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>版本号</version>
</dependency>
<!-- 版本号根据项目设计管理更改 1.4.0 -->
application.properties中配置shiro
#shiro配置
#默认初始路径
shiro.loginUrl=/safty/login/toLogin
#shiro 的 记住我 功能开启后的默认路径
shiro.successUrl=/safty/home/toHome
#认证失败后跳转路径
shiro.unauthorizedUrl=/safty/login/toLogin
#使用shiro本地session非HttpSession shiro会自动同步
shiro.userNativeSessionManager=true
#日志级别设置
#logging.level.全类名=debug
自定义Realm
package com.johe.scgcxx.base;
import java.text.MessageFormat;
import java.util.List;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import com.johe.scgcxx.dto.Curr_User;
import com.johe.scgcxx.dto.Menu;
import com.johe.scgcxx.model.Sys_Module;
import com.johe.scgcxx.model.Sys_User;
import com.johe.scgcxx.service.base.ShiroService;
@Configuration
public class DefaultShiro {
//日志对象
private static final Logger LOG = LoggerFactory.getLogger(DefaultShiro.class);
/**
* Bean注解名 必须为 authorizer
*/
@Bean("authorizer")
@Autowired
public AuthorizingRealm saftyRealm(ShiroService shiroService) {
return new AuthorizingRealm() {
/**
* 获取当前用户授权信息,shiro检查需要通过该方法获取授权信息 参数为 当前用户集合
* 该方法会在shiro 过滤方法中 配置的路径 进行权限认证时执行
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 获取用户账号
Curr_User currUser = (Curr_User) principals.getPrimaryPrincipal();// 获取首要当事人
// 创建授权信息对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// 查询用户权限,并放入授权对象
List<Menu> menuList = shiroService.getModuleByCurrUserID(currUser.getU_id());
for (Menu menu : menuList) {
simpleAuthorizationInfo.addStringPermission(String.valueOf(menu.getMenu_id()));
}
return simpleAuthorizationInfo;
}
/**
* 授权认证信息(当前用户信息,账号,密码) shiro在登录认证时需要通过该方法获取认证信息, 参数认证令牌,(一组用户名和密码)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
String u_id = (String) token.getPrincipal();
Sys_User user = shiroService.getCurrUser(u_id);
if (user == null) {
return null;
}
// 若该用户存在,创建当前用户
Curr_User currUser = new Curr_User(user.getU_id(), user.getU_name());
/**
* 创建认证信息 参数 1 当前用户 2认证凭证 3realm名称
* 该操作会将 当前用户存储到shiro内置对象principal中
*/
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(currUser, user.getU_pwd(), this.getName());
return info;
}
};
}
// 设置Shiro默认过滤器(应用启动时执行路径的权限过滤)
@Bean
@Autowired
public ShiroFilterChainDefinition shiroFilterChainDefinition(ShiroService shiroService) {
DefaultShiroFilterChainDefinition shiroFilter = new DefaultShiroFilterChainDefinition();
shiroFilter.addPathDefinition("/css/**", "anon");
shiroFilter.addPathDefinition("/elementui/**", "anon");
shiroFilter.addPathDefinition("/js/**", "anon");
shiroFilter.addPathDefinition("/safty/login/**", "anon");
// 加载动态权限
List<Sys_Module> moduleList = shiroService.getAllSubModules();
String PREMISSION_FORMAT = "authc,perms[{0}]";
// 由注入的资源管理对象获取所有资源数据,并且资源的authorities的属性是EAGER的fetch类型
for (Sys_Module sys_Module : moduleList) {
if (StringUtils.isEmpty(sys_Module.getM_url())) {
continue;
}
shiroFilter.addPathDefinition(sys_Module.getM_url().replace("toList", "**"),
MessageFormat.format(PREMISSION_FORMAT, String.valueOf(sys_Module.getM_id())));
}
// 其他资源必须经过认证
shiroFilter.addPathDefinition("/**", "authc");
LOG.debug("===========shiro校验==============");
LOG.debug(shiroFilter.getFilterChainMap().toString());
LOG.debug("===========shiro校验==============");
return shiroFilter;
}
}
登录与退出(ResultUtils为 自定义MAP结果返回,**Service为 自定义业务操作)
package com.johe.scgcxx.controller.safty;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.johe.scgcxx.base.Constants;
import com.johe.scgcxx.base.ResultUtils;
import com.johe.scgcxx.dto.Curr_User;
import com.johe.scgcxx.dto.Menu;
import com.johe.scgcxx.dto.Sys_User;
import com.johe.scgcxx.service.safty.SaftyLoginService;
//安全管理-用户登录
@Controller
public class SaftyLoginController {
@Autowired
private SaftyLoginService saftyLoginService;
@RequestMapping("/safty/login/doLogin")
@ResponseBody
public ResultUtils toLogin(@RequestBody Sys_User user,HttpSession session) {
try {
Subject subject = SecurityUtils.getSubject();
//创建登录令牌
UsernamePasswordToken token = new UsernamePasswordToken(user.getU_id(), user.getU_pwd());
//登录(该操作会执行realm中授权认证的方法)
subject.login(token);
//是否通过认证
if (subject.isAuthenticated()) {
//获取当前用户信息
Curr_User currUser = (Curr_User)subject.getPrincipal();
//将当前用户放入session
subject.getSession().setAttribute(Constants.SESSION_CURR_USER_ATTR, currUser);
System.out.println(user.getU_id()+":登录====进入");
return ResultUtils.successResult();
}
return ResultUtils.failResult("登录失败!!!");
} catch (UnknownAccountException e) {
return ResultUtils.failResult("用户名不存在!");
} catch (IncorrectCredentialsException e) {
return ResultUtils.failResult("账户密码 不正确!");
} catch (LockedAccountException e) {
return ResultUtils.failResult("用户名 被锁定 !");
}catch (Exception e) {
e.printStackTrace();
return ResultUtils.failResult("系统错误!");
}
}
@DeleteMapping("/safty/home/doLogout")
@ResponseBody
public ResultUtils toLogout() {
try {
//清除shiro内置session信息
SecurityUtils.getSubject().logout();
return ResultUtils.successResult();
} catch (Exception e) {
return ResultUtils.failResult("退出异常!!!");
}
}
}
shiro权限缓存设置 (无需对每次路由进行权限查询验证(相同路由只需首次进入时验证))
//该 权限缓存代码 位置 : Shiro自定义realm类中
//用于设置shiro缓存管理器
@Bean
protected CacheManager shiroCacheManager() {
return new MemoryConstrainedCacheManager();
}