<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.6.0version>
dependency>
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
dependency>
/**
* 权限配置文件
* @ClassName: ShiroConfiguration
* @author YoungJ
* @date 2020年8月25日
*
*/
@Configuration
public class ShiroConfig {
/**
* 这是shiro的大管家,相当于mybatis里的SqlSessionFactoryBean
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//登录
shiroFilterFactoryBean.setLoginUrl("/user/login");
//首页
shiroFilterFactoryBean.setSuccessUrl("/");
//错误页面,认证不通过跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
//页面权限控制
shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroFilterMapFactory.shiroFilterMap());
//自定义认证过滤器
/*Map filterMap = Maps.newHashMap();
//权限认证过滤器
filterMap.put("authc", new PermissionFilter());
shiroFilterFactoryBean.setFilters(filterMap);*/
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
/**
* web应用管理配置
* @param shiroRealm
* @param cacheManager
* @param manager
* @return
*/
@Bean
public DefaultWebSecurityManager securityManager(Realm shiroRealm,CacheManager cacheManager,RememberMeManager manager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setCacheManager(cacheManager);
securityManager.setRememberMeManager(manager);//记住Cookie
securityManager.setRealm(shiroRealm);
securityManager.setSessionManager(sessionManager());
return securityManager;
}
/**
* session过期控制
* @return
* @author fuce
* @Date 2019年11月2日 下午12:49:49
*/
@Bean
public DefaultWebSessionManager sessionManager() {
DefaultWebSessionManager defaultWebSessionManager=new DefaultWebSessionManager();
// 设置session过期时间3600s
Long timeout=60L*1000*60;//毫秒级别
defaultWebSessionManager.setGlobalSessionTimeout(timeout);
return defaultWebSessionManager;
}
/**
* 加密算法
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密
hashedCredentialsMatcher.setHashIterations(1);//加密次数
return hashedCredentialsMatcher;
}
/**
* 记住我的配置
* @return
*/
@Bean
public RememberMeManager rememberMeManager() {
Cookie cookie = new SimpleCookie("rememberMe");
cookie.setHttpOnly(true);//通过js脚本将无法读取到cookie信息
cookie.setMaxAge(60 * 60 * 24);//cookie保存一天
CookieRememberMeManager manager=new CookieRememberMeManager();
manager.setCookie(cookie);
return manager;
}
/**
* 缓存配置
* @return
*/
@Bean
public CacheManager cacheManager() {
MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
return cacheManager;
}
/**
* 配置realm,用于认证和授权
* @param hashedCredentialsMatcher
* @return
*/
@Bean
public AuthorizingRealm shiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher) {
MyShiroRealm shiroRealm = new MyShiroRealm();
//校验密码用到的算法
shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return shiroRealm;
}
/**
* 启用shiro方言,这样能在页面上使用shiro标签
* @return
*/
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
/**
* 启用shiro注解
*加入注解的使用,不加入这个注解不生效
*/
@Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
ShiroFilterMapFactory.java
package com.szcl.verify.shiro.config;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @ClassName: ShiroFilterMapFactory
* @author YoungJ
* @date 2020年8月26日
*
*/
public class ShiroFilterMapFactory {
public static Map<String, String> shiroFilterMap() {
// 设置路径映射,注意这里要用LinkedHashMap 保证有序
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//对所有用户认证
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/admin/login", "anon");
filterChainDefinitionMap.put("/admin/logout", "logout");
//放验证码
filterChainDefinitionMap.put("/captcha/**", "anon");
// 释放 druid 监控画面
filterChainDefinitionMap.put("/druid/**", "anon");
//释放websocket请求
filterChainDefinitionMap.put("/websocket", "anon");
//前端
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/index", "anon");
filterChainDefinitionMap.put("/quartz/**", "anon");
//开放APicontroller
filterChainDefinitionMap.put("/ApiController/**", "anon");
//对所有页面进行认证
filterChainDefinitionMap.put("/**","authc");
return filterChainDefinitionMap;
}
}
MyShiroRealm.java
/**
* 身份校验核心类
*
* @author YoungJ
* @date 2020年8月25日
*/
@Service
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private UserMapper tsysUserDao;
@Autowired
private PermissionMapper permissionDao;
@Autowired
private RoleMapper roleDao;
/**
* 认证登陆
*/
@SuppressWarnings("unused")
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
if (token.getPrincipal() == null) {
return null;
}
String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
// 通过username从数据库中查找 User对象
// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
User userInfo = tsysUserDao.queryUserName(username);
if (userInfo == null) {
return null;
} else {
//错误次数控制
checkLoginErrorTimes(username);
//单一登录控制
checkSingleSingOn(userInfo);
return new SimpleAuthenticationInfo(
userInfo,
userInfo.getPassword(),
getName()
);
}
}
/**
* 错误次数控制
* @param userName
*/
private void checkLoginErrorTimes(String userName) {
}
/**
* 单一登录控制
* @param user
*/
private void checkSingleSingOn(User user) {
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
//获取当前已登录的用户session列表
SessionDAO sessionDAO = sessionManager.getSessionDAO();
Collection<Session> sessions = sessionDAO.getActiveSessions();
for (Session session : sessions) {
//清除该用户以前登录时保存的session
Object obj = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
SimplePrincipalCollection coll = (SimplePrincipalCollection) obj;
if(coll !=null){
User userLogin = (User)coll.getPrimaryPrincipal();
if(user.getUsername().equals(userLogin.getUsername())){
sessionDAO.delete(session);
}
}
}
}
/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("principals should not be null");
}
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
User userinfo = (User) principals.getPrimaryPrincipal();
Long id = userinfo.getId();
List<Role> tsysRoles = roleDao.queryUserRole(id);
for (Role userrole : tsysRoles) {
Long rolid = userrole.getId();
authorizationInfo.addRole(userrole.getName());
List<Permission> permissions = permissionDao.queryRoleId(rolid);
for (Permission p : permissions) {
if (StringUtils.isNotEmpty(p.getPerms())) {
authorizationInfo.addStringPermission(p.getPerms());
}
}
}
return authorizationInfo;
}
/**
* 清理缓存权限
*/
public void clearCachedAuthorizationInfo() {
this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
}
ShiroUtils.java
/**
* shiro 工具类
*
* @author YoungJ
*/
public class ShiroUtils {
private ShiroUtils() {
}
/**
* 获取shiro subject
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午10:00:55
*/
public static Subject getSubjct() {
return SecurityUtils.getSubject();
}
/**
* 获取登录session
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午10:00:41
*/
public static Session getSession() {
return SecurityUtils.getSubject().getSession();
}
/**
* 退出登录
*
* @author YoungJ
* @Date 2019年11月21日 上午10:00:24
*/
public static void logout() {
getSubjct().logout();
}
/**
* 获取登录用户model
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午10:00:10
*/
public static User getUser() {
User user = null;
Object obj = getSubjct().getPrincipal();
if (!StringUtils.isEmpty(obj)) {
user = new User();
BeanUtils.copyBeanProp(user, obj);
}
return user;
}
/**
* set用户
*
* @param user
* @author YoungJ
* @Date 2019年11月21日 上午9:59:52
*/
public static void setUser(User user) {
Subject subject = getSubjct();
PrincipalCollection principalCollection = subject.getPrincipals();
String realmName = principalCollection.getRealmNames().iterator().next();
PrincipalCollection newPrincipalCollection = new SimplePrincipalCollection(user, realmName);
// 重新加载Principal
subject.runAs(newPrincipalCollection);
}
/**
* 清除授权信息
*
* @author YoungJ
* @Date 2019年11月21日 上午9:59:37
*/
public static void clearCachedAuthorizationInfo() {
RealmSecurityManager rsm = (RealmSecurityManager) SecurityUtils.getSecurityManager();
MyShiroRealm realm = (MyShiroRealm) rsm.getRealms().iterator().next();
realm.clearCachedAuthorizationInfo();
}
/**
* 获取登录用户id
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午9:58:55
*/
public static Long getUserId() {
User tsysUser = getUser();
if (tsysUser == null || tsysUser.getId() == null) {
throw new RuntimeException("用户不存在!");
}
return tsysUser.getId();
}
/**
* 获取登录用户name
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午9:58:48
*/
public static String getLoginName() {
User tsysUser = getUser();
if (tsysUser == null) {
throw new RuntimeException("用户不存在!");
}
return tsysUser.getUsername();
}
/**
* 获取登录用户ip
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午9:58:26
*/
public static String getIp() {
return getSubjct().getSession().getHost();
}
/**
* 获取登录用户sessionid
*
* @return
* @author YoungJ
* @Date 2019年11月21日 上午9:58:37
*/
public static String getSessionId() {
return String.valueOf(getSubjct().getSession().getId());
}
/**
* 缓存获取当前用户权限
*/
public static Set<String> getPermissions() {
Set<String> userPermissions =
(Set<String>) SecurityUtils.getSubject().getSession().getAttribute(Constants.SHIRO_PERMISSIONKEY);
return userPermissions;
}
/**
* 设置权限缓存
*/
public static void setPermissions(Set<String> userPermissions) {
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(Constants.SHIRO_PERMISSIONKEY, userPermissions);
}
/**
* 设置缓存
*/
public static void setCacheParam(String key, Object value) {
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute(key, value);
}
/**
* 获取缓存
*/
public static Object getCacheParam(String key) {
Session session = SecurityUtils.getSubject().getSession();
return session.getAttribute(key);
}
}
CREATE TABLE `t_sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`username` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户账号',
`password` varchar(50) CHARACTER SET utf8 DEFAULT NULL COMMENT '用户密码',
`nickname` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '昵称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='用户表';
CREATE TABLE `t_sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(20) DEFAULT NULL COMMENT '角色名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表';
CREATE TABLE `t_sys_role_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`sys_user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`sys_role_id` bigint(20) DEFAULT NULL COMMENT '角色id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色中间表';
CREATE TABLE `t_sys_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(20) DEFAULT NULL COMMENT '权限名称',
`descripion` varchar(50) DEFAULT NULL COMMENT '权限描述',
`url` varchar(255) DEFAULT NULL COMMENT '授权链接',
`is_blank` int(1) DEFAULT '0' COMMENT '是否跳转 0 不跳转 1跳转',
`pid` bigint(20) DEFAULT NULL COMMENT '父节点id',
`perms` varchar(255) DEFAULT NULL COMMENT '权限标识',
`type` int(1) DEFAULT NULL COMMENT '类型 0:目录 1:菜单 2:按钮',
`icon` varchar(255) DEFAULT NULL COMMENT '菜单图标',
`order_num` int(11) DEFAULT NULL COMMENT '排序',
`visible` int(1) DEFAULT NULL COMMENT '是否可见',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限表';
CREATE TABLE `t_sys_permission_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_id` bigint(20) DEFAULT NULL COMMENT '角色id',
`permission_id` bigint(20) DEFAULT NULL COMMENT '权限id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限中间表';
LoginController.java
@Controller
public class LoginController {
private static Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
private IPermissionService permissionService;
@GetMapping("/index")
public String index(HttpServletRequest request) {
Long userId = null;
try {
userId = ShiroUtils.getUserId();
} catch (Exception e) {
return "login";
}
if (userId == null) {
return "login";
}
//获取菜单栏
MenuTree tree= permissionService.getTreePerm(userId);
// log.info(GsonConvertUtil.toJson(tree));
request.getSession().setAttribute("tree", tree);
request.getSession().setAttribute("sessionUserName", ShiroUtils.getUser().getNickname());
return "home";
}
@GetMapping("/user/login")
public String login(ModelMap model) {
try {
if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) {
return "redirect:index";
} else {
return "login";
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return "login";
}
@PostMapping(value = "/user/login")
@ResponseBody
public BaseDTO<String> login(@RequestParam("username") String username,
@RequestParam("password") String password,
@RequestParam(value = "isRemeberMe", required = false) Boolean isRemeberMe,
Model model) {
try {
if ((null != SecurityUtils.getSubject() && SecurityUtils.getSubject().isAuthenticated()) || SecurityUtils.getSubject().isRemembered()) {
return DtoConvertUtil.toDTO(null, "登录成功", Constants.CODE_SUCCESS, true);
} else {
System.out.println("--进行登录验证..验证开始");
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token =new UsernamePasswordToken(username, password);
if (isRemeberMe != null && isRemeberMe) {
token.setRememberMe(true);
}
currentUser.login(token);
// 设置登录过期时间30分钟
SecurityUtils.getSubject().getSession().setTimeout(30 * 60 * 1000);
// model.addAttribute("RollVerification", V2Config.getRollVerification());
// System.out.println("V2Config.getRollVerification()>>>"+V2Config.getRollVerification());
return DtoConvertUtil.toDTO(null, "登录成功", Constants.CODE_SUCCESS, true);
}
}catch (UnknownAccountException e) {
log.error("[登录异常] >> 账户不存在 account : {}", username);
model.addAttribute("msg", "账号不存在");
return DtoConvertUtil.toDTO(null, "账号不存在", Constants.CODE_PARAMA_ERR, false);
} catch (IncorrectCredentialsException e) {
log.error("[登录异常] >> 密码错误 account : {}", username);
model.addAttribute("msg", "密码错误");
return DtoConvertUtil.toDTO(null, "密码错误", Constants.CODE_PARAMA_ERR, false);
} catch (LockedAccountException e) {
log.error("[登录异常] >> 账户已锁定 account : {}", username);
model.addAttribute("msg", "账户已锁定");
return DtoConvertUtil.toDTO(null, "账户已锁定", Constants.CODE_PARAMA_ERR, false);
} catch (DisabledAccountException e) {
log.error("[登录异常] >> 账户已停用 account : {}", username);
model.addAttribute("msg", "账户已停用");
return DtoConvertUtil.toDTO(null, "账户已停用", Constants.CODE_PARAMA_ERR, false);
} catch (Exception e) {
log.error("[登录异常] >> 未知异常 account : {}", username);
model.addAttribute("msg", "未知异常");
return DtoConvertUtil.toDTO(null, "未知异常", Constants.CODE_PARAMA_ERR, false);
}
}
@GetMapping("/loginout")
public String loginOut(HttpServletRequest request, HttpServletResponse response){
//在这里执行退出系统前需要清空的数据
ShiroUtils.logout();
return "redirect:/user/login";
}
}
controller请求权限判断
/**
* 修改保存用户
*/
//@Log(title = "修改保存用户", action = "1")
@ApiOperation(value = "修改保存用户", notes = "修改保存用户")
@RequiresPermissions("system:user:edit")
@PostMapping("/user/edit")
@ResponseBody
public BaseDTO<String> editSave(User user, @RequestParam(value = "roles") List<Long> roles) {
if (roles == null || roles.isEmpty()) {
return DtoConvertUtil.toDTO(null, "请选择角色", Constants.CODE_NO_PERMISSION, false);
}
userService.updateUserRoles(user, roles);
return DtoConvertUtil.toDTO(null, "保存成功", Constants.CODE_SUCCESS, true);
}
页面权限判断
<html class="x-admin-sm" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<shiro:hasPermission name="system:user:edit">
shiro:hasPermission>