使用Springboot+shiro+layui打造漂亮简洁的权限控制系统

tech2026-04-10  2

Springboot集成shiro

pom.xml

<!-- shiro权限 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency> <!-- shiro权限支持thymeleaf --> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>

新建shiro配置

/** * 权限配置文件 * @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<String, Filter> 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>

展示效果

最新回复(0)