关于shiro:使用SpringbootShiroJwt完成用户登录授权认证

我的项目github地址:https://github.com/liboshuai0...我的项目gitee地址:https://gitee.com/liboshuai01...背景公司用的我的项目是基于shiro + cookie/session的,然而当初微服务架构的背景下都是采纳token机制进行认证和受权的。于是决定先本人搭建一个spring+shiro+jwt的我的项目,用来不便替换公司的技术栈。 Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,能够记录会话信息。而 Token 是令牌,拜访资源接口(API)时所须要的资源凭证。Token 使服务端无状态化,不会存储会话信息。 Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个申请都有签名还能避免监听以及重放攻打,而 Session 就必须依赖链路层来保障通信平安了。如果你须要实现有状态的会话,依然能够减少 Session 来在服务器端保留一些状态。 所谓 Session 认证只是简略的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是平安的。而 Token ,如果指的是 OAuth Token 或相似的机制的话,提供的是 认证 和 受权 ,认证是针对用户,受权是针对 App 。其目标是让某 App 有权力拜访某用户的信息。这里的 Token 是惟一的。不能够转移到其它 App上,也不能够转到其它用户上。Session 只提供一种简略的认证,即只有有此 SessionID ,即认为有此 User 的全副权力。是须要严格窃密的,这个数据应该只保留在站方,不应该共享给其它网站或者第三方 App。所以简略来说:如果你的用户数据可能须要和第三方共享,或者容许第三方调用 API 接口,用 Token 。如果永远只是本人的网站,本人的 App,用什么就无所谓了。 疾速开始搭建一个springboot我的项目demo我的项目pom.xml配置文件 父工程pom.xml文件 <?xml version="1.0" encoding="UTF-8"?><project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.liboshuai</groupId> <artifactId>mall-tiny</artifactId> <version>1.0-SNAPSHOT</version> <modules> <module>mall-tiny-01</module> <module>mall-tiny-00-api</module> </modules> <packaging>pom</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <mybatis-plus-boot-starter-version>3.4.0</mybatis-plus-boot-starter-version> <druid-spring-boot-starte-version>1.2.11</druid-spring-boot-starte-version> <mysql-connector-java-version>8.0.15</mysql-connector-java-version> <lombok-version>1.18.10</lombok-version> <log4j-version>1.2.17</log4j-version> <springfox-swagger2-version>2.7.0</springfox-swagger2-version> <springfox-swagger-ui-version>2.7.0</springfox-swagger-ui-version> <jackson-databind-version>2.13.3</jackson-databind-version> <xxl-job-core-version>2.4.0-SNAPSHOT</xxl-job-core-version> <hutool-all-version>4.5.7</hutool-all-version> <jjwt-version>0.9.0</jjwt-version> <mybatis-plus-generator-version>3.5.1</mybatis-plus-generator-version> <velocity-engine-core-version>2.3</velocity-engine-core-version> <commons-io-version>2.4</commons-io-version> <shiro-version>1.4.0</shiro-version> <jwt-version>3.2.0</jwt-version> <fastjson.version>1.2.58</fastjson.version> <knife4j-swagger-version>2.0.4</knife4j-swagger-version> </properties></project>子工程pom.xml文件 ...

September 24, 2022 · 18 min · jiezi

关于shiro:Shiro学习笔记一-基本概念与使用

Shiro能帮忙咱们干什么?Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications. -《Apache Shiro 官网》 Apache Shiro 是Java畛域内的一款简略易用而又弱小的一款平安框架,次要用于登录验证、受权、加密、会话治理。Shiro领有简略利用的API,您能够快送的应用它来爱护您的利用,不论是小到手机利用还是到大的Web企业级利用。 从下面的一段话咱们能够提取到以下信息: Shiro简略易用而又弱小次要利用于登录验证、受权、加密、会话治理。第一点须要在中应用中缓缓领会,第二点是咱们次要关注的,这里咱们一点一点的讲。 登录验证 authentication 与 会话治理这里咱们回顾一下HTTP协定和Servlet, 晚期的HTTP协定是无状态的, 这个无状态咱们能够这么了解,你一分钟前拜访和当初拜访一个网站,服务端并不意识你是谁,然而对于Web服务端开发者来说, 这便无奈实现访问控制,某些信息只能登录用户能力看,各自看各自的。这着实的有点限制住了Web的倒退,为了让HTTP协定有状态,RFC-6235提案被取得批准,这个提案引入了Cookie, 是服务器发送到用户浏览器并保留在本地的一小块数据,它会再浏览器下次向同一服务器再发动申请时被携带并发送到服务器上,服务端就能够实现“辨认”用户了。 在原生的Servlet场景中如下: 代码示例: /** * 拦挡所有的申请 */@WebFilter(urlPatterns = "/*")public class LoginCheckFilter implements Filter { /** * 不重写init办法,过滤器无奈起作用 * @param filterConfig * @throws ServletException */ @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("----login check filter init-----"); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; HttpSession session = servletRequest.getSession(); // 这里采取了临时写死,目前只放行登录申请的URL String requestUri = servletRequest.getRequestURI(); if ("/login".equals(requestUri)){ // 放行,此申请进入下一个过滤器 chain.doFilter(request,response); }else { Object attribute = session.getAttribute("currentUser"); if (Objects.nonNull(attribute)){ chain.doFilter(request,response); }else { request.getRequestDispatcher("/login.jsp").forward(request,response); } } }}@WebFilter(urlPatterns = "/login")public class LoginFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { System.out.println("------login filter init-----"); } /** * 由登录过滤器来执行登录验证操作 * 要求用户名和明码不为空的状况下才进行下一步操纵 * 此处省略判空操作 * 只做模仿登录 */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; String userName = (String) servletRequest.getAttribute("userName"); String password = (String)servletRequest.getAttribute("password"); // 这里伪装去数据库去查账号和明码 HttpSession session = servletRequest.getSession(); // 生成session, session.setAttribute("currentUser",userName + password); // 放到下一个过滤器,如果这是最初一个过滤器,那么这个申请会被放行到对应的Servlet中 chain.doFilter(request,response); }}@WebServlet(value = "/hello")public class HttpServletDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("hello world"); super.doGet(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doPost(req, resp); }}后面提到, 为了实现让HTTP协定有“状态”, 浏览器引入了Cookie, 寄存服务端发送给客户端的数据, 为了使服务端辨别不同的用户, 服务端程序引入了Session这个概念,浏览器首次申请服务端,服务端会在HTTP协定中通知客户端,须要再Cooke外面记录一个SessionID,当前每次申请把这个SessionId发送到服务器,这样服务器就能辨别不同的客户端了。 ...

June 11, 2022 · 6 min · jiezi

关于shiro:JWTShiro认证

JWTJWT调试工具https://jwt.io/#debugger 全称:Json Web Token 什么是JWT,是为了在网络应用环境间传递申明而执行的一种基于JSON的凋谢规范(RFC 7519),该token被设计为紧凑且平安的 ,特地实用于分布式站点的单点登录(SSO)常见 JWT个别作为用户信息在客户端和服务端之间传递,以便于从资源服务器获取资源,也能够减少一些额定的其余业务逻辑所必须的申明信息,该token也能够间接用于认证,也能够被加密 JWT+Shiro进行登录认证外围类一:ShiroConfig办法一:配置SecurityManager,这个类是Shiro的外围类,用于治理所有用户 @Bean public DefaultWebSecurityManager securityManager(AccountRealm accountRealm, SessionManager sessionManager, RedisCacheManager redisCacheManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(accountRealm); securityManager.setSessionManager(sessionManager); securityManager.setCacheManager(redisCacheManager); /* * 敞开shiro自带的session,详情见文档 */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); return securityManager; }办法二:配置过滤器链 @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); //将所有申请交给名为"jwt"的过滤器链进行解决 //在下一个办法咱们会看到“jwt"过滤器链由自定义的JwtFilter的实例jwtFilter实现 chainDefinition.addPathDefinition("/**", "jwt");// 次要通过注解形式校验权限,这里都用jwtfilter进行拦挡 return chainDefinition; }办法三:配置过滤器工厂,为过滤器工厂配置过滤器链以及过滤器 @Bean("shiroFilterFactoryBean")public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, ShiroFilterChainDefinition shiroFilterChainDefinition) { ShiroFilterFactoryBean shiroFilterFactory = new ShiroFilterFactoryBean(); shiroFilterFactory.setSecurityManager(securityManager); Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", jwtFilter); //设置filter为自定义的jwtFilter shiroFilterFactory.setFilters(filters); Map<String, String> filterMap = shiroFilterChainDefinition.getFilterChainMap(); shiroFilterFactory.setFilterChainDefinitionMap(filterMap); return shiroFilterFactory;}上面是这个办法的一些解释 ...

March 10, 2021 · 2 min · jiezi

关于shiro:超详细附源码SpringBootshiromybatisThymeleaf实现权限登录

最近在做一个期末作品,就是应用ssm+thymeleaf+vue+shiro实现一个具备权限登录,且能实现用户信息增删查改的这么一个我的项目,上面仅仅是实现权限认证和登录。为什么我选shiro,而不选spring Security,是因为我试过,security切实是比拟难,封装的太厉害了,哈哈哈哈,所以果决放弃,抉择shiro进行。 下一篇还实现了增删查改,应用vue,然而没有前后端拆散,博客链接 !github源码连贯,须要请自行下载。 提醒,这个我的项目曾经有了增删查改,跟着上面的博客做,也能做进去页面跳转权限,然而没有增删查改。 以下是学习shiro的一个小Demo: 1.首先是底层数据库:-- ------------------------------ Table structure for role-- ----------------------------CREATE TABLE `role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色表主键', `role_name` varchar(32) DEFAULT NULL COMMENT '角色名称', PRIMARY KEY (`id`));-- ------------------------------ Records of role-- ----------------------------INSERT INTO `role` VALUES (1, 'SUPER_ADMIN');INSERT INTO `role` VALUES (2, 'ADMIN');INSERT INTO `role` VALUES (3, 'USER');-- ------------------------------ Table structure for user-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键', `username` varchar(32) NOT NULL COMMENT '用户名', `password` varchar(32) NOT NULL COMMENT '明码', `role_id` int(11) DEFAULT NULL COMMENT '与role角色表分割的外键', PRIMARY KEY (`id`), CONSTRAINT `user_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`));-- ------------------------------ Records of user-- ----------------------------INSERT INTO `user` VALUES (1, 'BWH_Steven', '666666', 1);INSERT INTO `user` VALUES (2, 'admin', '666666', 2);INSERT INTO `user` VALUES (3, 'zhangsan', '666666', 3);-- ------------------------------ Table structure for permission-- ----------------------------CREATE TABLE `permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限表主键', `permission_name` varchar(50) NOT NULL COMMENT '权限名', `role_id` int(11) DEFAULT NULL COMMENT '与role角色表分割的外键', PRIMARY KEY (`id`), CONSTRAINT `permission_role_on_role_id` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`));-- ------------------------------ Records of permission-- ----------------------------INSERT INTO `permission` VALUES (1, 'user:*', 1);INSERT INTO `permission` VALUES (2, 'user:*', 2);INSERT INTO `permission` VALUES (3, 'user:queryAll', 3);2.创立spring boot我的项目,用maven构建创立实体类(User,Role,Permissions):User: ...

November 30, 2020 · 7 min · jiezi

关于shiro:权限管理

一、权限框架介绍1. 权限治理实质:用户身份认证+受权流程:用户首先通过身份认证,通过后即可拜访该资源1.1 用户身份认证 1.2 受权流程 2. 权限框架Shiro和Spring Security比拟(1)Shiro比Spring更容易应用,实现和了解(2)Spring Security有更好的社区反对(3)Apache Shiro在Spring Security解决密码学方面有一个额定的模块(4)Spring-security 对spring 联合较好,如果我的项目用的springmvc ,应用起来很不便。然而如果我的项目中没有用到spring,那就不要思考它了。(5)Shiro 功能强大、且 简略、灵便。是Apache 下的我的项目比拟牢靠,且不跟任何的框架或者容器绑定,能够独立运行 二、Shiro根底介绍1. Shiro三个外围组件1.1 Subject(以后操作用户) 不仅仅指人,也能够是第三方过程、后盾帐户(Daemon Account)或其余相似事物。Subject代表了以后用户的平安操作,SecurityManager则治理所有用户的平安操作。 1.2 SecurityManager(保护核心) Shiro框架的外围,典型的Facade模式,用来协调外部组件实例,并提供平安治理的各种服务。 1.3 Realm(数据中心) 对用户认证(登录)和受权(访问控制)时,Shiro会从配置的Realm中查找用户及其权限信息。 当配置Shiro时,至多指定一个Realm,用于认证和(或)受权。配置多个Realm是能够的,然而至多须要一个。 Shiro内置了能够连贯大量平安数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、相似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需要,你还能够插入代表自定义数据源的本人的Realm实现。 2. Shiro相干类介绍(1)Authentication 认证 ---- 用户登录(2)Authorization 受权 --- 用户具备哪些权限(3)Cryptography 平安数据加密(4)Session Management 会话治理(5)Web Integration web系统集成(6)Interations 集成其它利用,spring、缓存框架 3. Shiro 特点(1)易于了解的 Java Security API;(2)简略的身份认证(登录),反对多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);(3)对角色的简略的签权(访问控制),反对细粒度的签权;(4)反对一级缓存,以晋升应用程序的性能;(5)内置的基于 POJO 企业会话治理,实用于 Web 以及非 Web 的环境;(6)异构客户端会话拜访;(7)非常简单的加密 API;(8)不跟任何的框架或者容器捆绑,能够独立运行 三、Spring Boot整合Shiro代码实战3.1 pom.xml中增加依赖 <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version></dependency>3.2 编写测试Controller类 ...

October 15, 2020 · 3 min · jiezi

关于shiro:shiro

shiro学习关联关系

October 12, 2020 · 1 min · jiezi

关于shiro:Shiro使用之记住用户会话显示用户名

Shiro应用之记住用户、会话、显示用户名 在登录用户时,点击记住我,从而下次登录时能够间接登录一、记住用户1、在SpringShiroConfig配置rememberMe对象 /** * 配置记住我的管理器对象 */ @Bean public RememberMeManager rememberMeManager() { CookieRememberMeManager cManager = new CookieRememberMeManager(); // 用户信息保留在cookie中 SimpleCookie cookie = new SimpleCookie("rememberMe"); // 保留工夫 cookie.setMaxAge(7 * 24 * 60 * 60); cManager.setCookie(cookie); return cManager; }2、将rememberMe注入securityManager @Beanpublic org.apache.shiro.mgt.SecurityManager securityManager(Realm realm, CacheManager cacheManager, RememberMeManager rememberMeManager) { DefaultWebSecurityManager sManager = new DefaultWebSecurityManager(); // 写完realm后把它注入给securityManager sManager.setRealm(realm); sManager.setCacheManager(cacheManager); sManager.setRememberMeManager(rememberMeManager); return sManager;}3、Controller层增加rememberMe判断 @RequestMapping("doLogin")public JsonResult doLogin(boolean isRemember, String username, String password) { // 获取subject对象,负责提交客户端的账号信息 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 记住我 if (isRemember) { token.setRememberMe(true); } // 给securityManager提交用户信息 subject.login(token); return new JsonResult("login ok");}4、在ShiroFilterFactoryBean中增加rememberMe登录权限 ...

September 21, 2020 · 1 min · jiezi

关于shiro:Shiro使用之权限缓存

Shiro应用之权限缓存 咱们每次应用权限的时候,每次都要从数据库中查找权限数据,影响零碎的运行效率,所以能够对用户的权限进行缓存。1、在SpringShiroConfig中配置缓存 /** * 配置shiro框架中的CacheManager对象,用于缓存用户权限 * * @return */ @Bean public CacheManager shiroCacheManager() { return new MemoryConstrainedCacheManager(); }2、将缓存注入到securityManager中@Beanpublic org.apache.shiro.mgt.SecurityManager securityManager(Realm realm, CacheManager cacheManager, RememberMeManager rememberMeManager, SessionManager sessionManager) { DefaultWebSecurityManager sManager = new DefaultWebSecurityManager(); // 写完realm后把它注入给securityManager sManager.setRealm(realm); sManager.setCacheManager(cacheManager); sManager.setRememberMeManager(rememberMeManager); sManager.setSessionManager(sessionManager); return sManager; }3、后果 能够看到当我第一次点击日志治理的时候第一次拜访,受权中的输入执行了。第二次点击能够看到语句没有输入,阐明是从缓存中获取的权限。

September 21, 2020 · 1 min · jiezi

关于shiro:Shiro使用之业务授权

Shiro应用业务受权 业务受权中关系到几个表中的对应关系,要依据用户的角色是什么,角色的权限有哪些来给用户调配权限。 1、配置文件在SpringShiroConfig(见认证文章)中配置advisor对象如下内容:shiro框架底层会依据此对象的matchs返回值判断是否创立代理对象。 @Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor;}2、dao层基于用户的登陆信息,获取用户的权限信息 依据用户id查找角色id信息;在UserRoleDao中增加一下代码:List<Integer> findRoleIdsByUserId(Integer id); 映射文件<select id="findRoleIdsByUserId" resultType="int"> select role_id from sys_user_roles where user_id=#{id}</select>依据查到的角色ids查问菜单idsList<Integer> findMenuIdsByRoleIds(Integer[] roleIds); 映射文件 <select id="findMenuIdsByRoleIds" resultType="int"> select menu_id from sys_role_menus where role_id in <foreach collection="roleIds" item="role_id" open="(" close=")" separator=","> #{role_id} </foreach> </select>在依据查到的菜单ids查找权限标识办法List<String> findPermissions(Integer[] menuIds); <select id="findPermissions" resultType="string"> select premission from sys_menus where id in <foreach collection="menuIds" item="menuid" open="(" close=")" separator=","> #{menuid} </foreach></select>3、重写ShiroUserRealm中的受权办法 /** * 受权 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 1.获取用户登陆信息 SysUser user = (SysUser) principals.getPrimaryPrincipal(); Integer id = user.getId(); // 2.基于用户id查问领有的角色id List<Integer> roleIds = sysUserRolesMapper.findRoleIdsByUserId(id); // 3。基于角色id查找菜单id List<Integer> menuIds = sysRoleMenuMapper.findMenuIdsByRoleIds(roleIds); // 4。基于菜单id获取权限标识符 List<String> premissions = sysMenuMapper.findPermissions(menuIds); // 5.封装权限标识符并返回 Set<String> set = new HashSet<>(); for (String pre : premissions) { if (!StringUtils.isEmpty(pre)) { set.add(pre); } } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(set); return info; }4、增加注解 @RequiresPermissions(“权限标识”) ...

September 19, 2020 · 1 min · jiezi

关于shiro:Shiro的使用之认证拦截

Shiro的应用之认证拦挡 Shiro是一个平安开发框架,它把零碎的平安认证等相干性能抽取进去,升高了零碎的开发成本。 一 Shiro实现认证拦挡 步骤: 1 零碎调用subject的login办法将用户信息传递给securityManager 2 securityManager将认证操作委托给Authenticator 3 Authenticator将用户输出的身份信息发送给Realm 4 Realm拜访数据库获取用户信息并将信息封装并返回 5 Authenticator对封装的信息进行身份认证 输出用户信息后登陆到零碎界面 1.须要的依赖<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version></dependency>2. Shiro的外围配置对象在springboot中没有为咱们主动配置shiro,所以要手动配置 首先创立一个配置类:SpringShiroConfig类;而后在配置类中增加SecurityManager接口对象;在配置类中增加一个ShiroFilterFactoryBean对象;通过此对象实现对匿名拜访和认证拜访代码如下:package com.py.pj.common.config;import java.util.LinkedHashMap;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.realm.Realm;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/** * @author WL * @version 创立工夫:2020-9-19 13:24:57 * @Description Shiro的外围配置对象 * @Configuration 此注解形容的类是一个配置对象,此对象会交给spring解决 * @Bean 此注解形容的类会被交给spring解决,由spring为咱们创建对象 */@Configurationpublic class SpringShiroConfig { @Bean public SecurityManager securityManager(Realm realm) { DefaultWebSecurityManager dsm = new DefaultWebSecurityManager(); dsm.setRealm(realm); return dsm; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean sffBean = new ShiroFilterFactoryBean(); sffBean.setSecurityManager(securityManager); // 设置登录界面url sffBean.setLoginUrl("/doLogin"); LinkedHashMap<String, String> map = new LinkedHashMap<>(); map.put("/bower_components/**", "anon"); map.put("/modules/**", "anon"); map.put("/dist/**", "anon"); map.put("/plugins/**", "anon"); // 用户登录界面容许匿名拜访 map.put("/user/doLogin", "anon"); // 退出登陆 map.put("/doLogout", "logout"); // 除了动态资源,其余的都要认证 map.put("/**", "authc"); sffBean.setFilterChainDefinitionMap(map); return sffBean; }}3.业务模块实现 本模块的业务在realm模块中实现3.1 依据用户名搜寻用户信息在SysUserMapper中创立findUserByUsername(String username)办法 ...

September 19, 2020 · 2 min · jiezi

关于shiro:9月18号笔记Shiro安全框架

1. Shiro概述-------Shiro是apache旗下一个开源平安框架(http://shiro.apache.org/),它将软件系统的平安认证相干的性能抽取进去,实现用户身份认证,权限受权、加密、会话治理等性能,组成了一个通用的平安认证框架。应用shiro就能够十分疾速的实现认证、受权等性能的开发,升高零碎老本。如图: 1.1 2. Shiro概要架构 --------- 留神:1).Subject 主体对象,负责提交用户认证和受权信息;2).SecuritvManager:平安管理器,负责认证,受权等业务实现;3).Realm:畛域,负责从数据层获取业务数据; 1.1.3 1. Shiro具体架构hiro框架进行权限治理时,要波及到的一些外围对象,次要包含:认证治理对象,受权治理对象,会话治理对象,缓存治理对象,加密治理对象以及Realm治理对象(畛域对象:负责解决认证和受权畛域的数据访问题)等,其具体架构如图-3所示: 2.2. Shiro框架认证拦挡实现(filter)2.2.2步骤实现:1.增加依赖: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version></dependency>2.2. Shiro外围对象配置2.2.1 第一步:创立SpringShiroConfig类。要害代码如下: package com.cy.pj.common.config;/**@Configuration 注解形容的类为一个配置对象, * 此对象也会交给spring治理 */@Configurationpublic class SpringShiroConfig {}第二步:在Shiro配置类中增加SecurityManager配置(这里肯定要应用org.apache.shiro.mgt.SecurityManager这个接口对象),要害代码如下: @Beanpublic SecurityManager securityManager() { DefaultWebSecurityManager sManager= new DefaultWebSecurityManager(); return sManager;}第三步: 在Shiro配置类中增加ShiroFilterFactoryBean对象的配置。通过此对象设置资源匿名拜访、认证拜访。要害代码如下: @Beanpublic ShiroFilterFactoryBean shiroFilterFactory ( SecurityManager securityManager) { ShiroFilterFactoryBean sfBean= new ShiroFilterFactoryBean(); sfBean.setSecurityManager(securityManager); //定义map指定申请过滤规定(哪些资源容许匿名拜访,哪些必须认证拜访) LinkedHashMap<String,String> map= new LinkedHashMap<>(); //动态资源容许匿名拜访:"anon" map.put("/bower_components/**","anon"); map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); //除了匿名拜访的资源,其它都要认证("authc")后拜访 map.put("/**","authc"); sfBean.setFilterChainDefinitionMap(map); return sfBean; }第四步:在PageController中增加一个出现登录页面的办法,要害代码如下: ...

September 18, 2020 · 2 min · jiezi

关于shiro:00034shiro的配置和使用

jar: <shiro.version>1.2.3</shiro.version><!-- shiro --><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version></dependency><dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version></dependency>自定义自定义Realm: import com.xmdishi.fmp.model.po.business.BusinessMenuPo;import com.xmdishi.fmp.model.po.business.BusinessUserPo;import com.xmdishi.fmp.model.qo.business.BusinessUserQo;import com.xmdishi.fmp.service.business.BusinessMenuService;import com.xmdishi.fmp.service.business.BusinessUserService;import org.apache.shiro.authc.*;import org.apache.shiro.authc.credential.CredentialsMatcher;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;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.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.context.annotation.Bean;import org.springframework.stereotype.Component;import java.util.HashSet;import java.util.List;import java.util.Set;/** * 自定义Realm * @author cjianquan * @date 2020/4/6 * @param * @return */@Componentpublic class CustomRealm extends AuthorizingRealm { @Autowired private BusinessUserService businessUserService; @Autowired private BusinessMenuService businessMenuService; private static Logger logger=LoggerFactory.getLogger(CustomRealm.class); public CustomRealm() { logger.info("CustomRealm===================="); } @Override public String getName() { return "CustomRealm"; } @Bean(name = "credentialsMatcher") public HashedCredentialsMatcher credentialsMatcher(){ HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("md5"); credentialsMatcher.setHashIterations(1); return credentialsMatcher; } @Override public void setCredentialsMatcher(@Qualifier("credentialsMatcher")CredentialsMatcher credentialsMatcher){ super.setCredentialsMatcher(credentialsMatcher); } /** * realm受权办法 从输出参数principalCollection失去身份信息 依据身份信息到数据库查找权限信息 将权限信息增加给受权信息对象 * 返回 受权信息对象(判断用户拜访url是否在权限信息中没有体现) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { BusinessUserPo user = (BusinessUserPo) principalCollection.getPrimaryPrincipal(); String roleIds = user.getRoleIds(); List<String> btnList = null; try{ btnList = businessMenuService.queryBtnsByRoles(roleIds); }catch (Exception e){ e.printStackTrace(); } // 用户权限列表 Set<String> permsSet = new HashSet<String>(); if(btnList!=null && btnList.size()>0){ permsSet.addAll(btnList); } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permsSet); return info; } /** * 表单认证过滤器认证时会调用自定义Realm的认证办法进行认证,胜利回到index.do,再跳转到index.jsp页面 * * 前提:表单认证过滤器收集和组织用户名和明码信息封装为token对象传递给此办法 * * token:封装了身份信息和凭证信息 2步骤:比对身份 信息;比对凭证 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); String password = new String((char[]) token.getCredentials()); // 查问用户信息 BusinessUserQo userQo = new BusinessUserQo(); userQo.setUserName(username); List<BusinessUserPo> userList = null; BusinessUserPo businessUserPo = null; try{ userList = this.businessUserService.query(userQo); if(userList!=null && userList.size()>0){ businessUserPo = userList.get(0); } }catch (Exception e){ e.printStackTrace(); } // 账号不存在 if (businessUserPo == null ) { throw new UnknownAccountException("账号不存在!"); } // 明码谬误 if (!password.equals(businessUserPo.getPassword())) { throw new IncorrectCredentialsException("账号或明码不正确!"); } // 账号未调配角色 if (businessUserPo.getRoleIds() == null ) { throw new UnknownAccountException("账号未调配角色!"); } //cjianquan 2020/2/8 登录胜利,查问菜单 try{ List<BusinessMenuPo> menuList = this.businessMenuService.queryByRoles(businessUserPo.getRoleIds()); businessUserPo.setMenuList(menuList); }catch (Exception e){ e.printStackTrace(); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(businessUserPo, password, getName()); return info; }}web.xml 增加 : ...

September 17, 2020 · 3 min · jiezi

关于shiro:shiro理解

shiro框架认证模块AuthenticationInfo同时还得配置controller层,应用subject//1.须要从token中取得用户名级明码怎么办?UsernamePasswordToken Utoken = (UsernamePasswordToken)token; //token强转成子类对象可取得String username = Utoken.getUsername();token(将token类型强转为他的子类usernamepasswordToken类型),以便获取账户和明码//2.从数据库查问?通过以后用户的登录的username查询数据库是否有对应的用户信息SysUsersinsert sysusers = sysuserdao.selectById(username);//3.对查问构造进行判断?if(sysusers==null) throw new UnknownAccountException();//没有找到异样//3.1判断是否被锁定?if(sysusers.getvalid()==0) throw new LockedAccountException();//用户名被锁定异样/4.封装用户信息并返回.?不晓得返回的是什么,看办法的返回值//因为盐值传递的必须是要转换一下ByteSource credentialsSalt = ByteSource.Util.bytes(sysusers.getSalt());SimpleAuthenticationInfo info = new SimpleAuthenticationInfo( sysusers, // 用户身份 sysusers.getPassword(), //数据库的明码(已被加密) credentialsSalt, //盐值 getName());//认证对象返回 return info; 受权模块AuthorizationInfo获取以后对象 SysUsersinsert user =( //获取以后类对强转一下像转换 SysUsersinsert)principals.getPrimaryPrincipal();基于菜单id查问受权标识并校验留神须要受权的业务在业务办法上退出注解@RequiresPermissions("sys:user:update")List<String> menusSTRING = sysmenudao.findMenusStringprop(menuids); if(menusSTRING==null||menusSTRING.size()==0) throw new AuthorizationException();封装查问后果并返回SimpleAuthorizationInfo sim = new SimpleAuthorizationInfo(); sim.setStringPermissions(stringPermissions); return sim;//因为 sim.setStringPermissions(stringPermissions) 须要传入一个List类型所以创立一个汇合Set<String> stringPermissions=new HashSet<>(); for (String pre : menusSTRING) { if(pre!=null&&!"".equals(pre)) { stringPermissions.add(pre); } } 还须要配置凭证匹配器//设置凭证匹配器:还有一种形式为get @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { //构建匹配器对象 HashedCredentialsMatcher credent = new HashedCredentialsMatcher(); //设置加密算法 credent.setHashAlgorithmName("MD5"); //设计几次加密与增加用户时雷同次数 credent.setHashIterations(1); super.setCredentialsMatcher(credent); }shiro框架的手动配置超级管理员SecurityManager//Realm 是认证和受权的两个实现类的一个大接口益处是实现了多态缩小代码的编写@Beanpublic SecurityManager SecurityManager(Realm realm){ DefaultWebSecurityManager securityManager= new DefaultWebSecurityManager(); securityManager.setRealm(realm); return securityManager; }//如果须要配置缓存将缓存对象也得注入给securityManager对象 CacheManager 缓存对象RememberMeManager 记住我对象 //将两个同时注入到SecurityManager之中 ji'ke //增加shiro框架的cache缓存 @Bean public CacheManager newcacheMange() { return new MemoryConstrainedCacheManager(); }受权治理配置/** 受权治理操作时 * 首先在springboot我的项目中只须要配置Advison,其余两个配置由spring底层主动配置 * 配置shiro中的Advison对象,此对象在spring框架启动时用于告知spring框架要为那些切入点形容对象创立代理对象 */@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor; }手动增加认证跳转信息(模板)//设置通过此对象设置资源匿名拜访、认证拜访。要害代码如下:@Beanpublic ShiroFilterFactoryBean shiroFilterFactory (SecurityManager securityManager) { ShiroFilterFactoryBean sfBean = new ShiroFilterFactoryBean(); sfBean.setSecurityManager(securityManager);//定义map指定申请过滤规定(哪些资源容许匿名拜访,哪些必须认证拜访) sfBean.setLoginUrl("/doLoginUI"); //设置认证页LinkedHashMap<String,String> map= new LinkedHashMap<>(); //动态资源容许匿名拜访:"anon" map.put("/bower_components/**","anon"); map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); map.put("/user/doLogin","anon"); map.put("/doLogout","anon");//除了匿名拜访的资源,其它都要认证("authc")后拜访 map.put("/**","user"); sfBean.setFilterChainDefinitionMap(map); //间接退出map也能够一个的加 return sfBean; }应用增加依赖包的形式配置shiro@Configuration//此注解形容的类为spring的配置类,不必把那边的业务交给spring治理了@Configurationpublic class SpringShiroConfig{@Bean public Realm realm() { return new shiroAuthenSerivce(); }//配置认证转换器@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); //同上能够是Map汇合也一个一个的增加,最好用map chainDefinition.setaddPathDefinitions(map);}//如果想要配置cache缓存、话间接配置一下代码即可@Beanprotected CacheManager shirocacheManager() { return new MemoryConstrainedCacheManager(); }

August 26, 2020 · 1 min · jiezi

关于shiro:shiro配置

在之前的文章中,也应用过shiro平安框架,然而应用过程中,感觉进行了大量的配置很是繁琐也不好记忆,当初在官网上有给定的配置格局,咱们能够一起来试一下, 地址:http://shiro.apache.org/spring-boot.html 关上后不必看第一个Standalone Applications,这是配置相似本地利用类的. 间接看Web Applications, 首先增加依赖<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.6.0</version> </dependency>2.依据官网给出的格局配置shiro配置类 @Configuration //此注解形容的类为spring容器中的一个配置类public class SpringShiroConfig { @Bean public Realm realm() { return new ShiroUserRealm(); } /**配置过滤规定*/ @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); LinkedHashMap<String,String> map= new LinkedHashMap<>(); //动态资源容许匿名拜访:"anon" map.put("/bower_components/**","anon"); map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); map.put("/user/doLogin","anon"); map.put("/doLogout","logout");//logout对应着shiro框架中退出过滤器 //除了匿名拜访的资源,其它都要认证("authc")后拜访 map.put("/**","authc");//如果须要实现记住我性能,这里的过滤器标识应用user chainDefinition.addPathDefinitions(map); return chainDefinition; } /** * 配置缓存对象,用于缓存用户的权限信息 * @return */ @Bean protected CacheManager shiroCacheManager() { return new MemoryConstrainedCacheManager(); }}留神:其中Realm具体的类以及其中的业务都是要咱们本人来写的;拜访以及拦挡的资源也是要咱们依据理论状况来写,而后放入map中进行返回的;以上就是重要的两点配置 ...

August 24, 2020 · 1 min · jiezi

关于shiro:shiro安全框架

简介Shiro是apache旗下一个开源平安框架,将软件系统的平安认证相干的性能:身份认证,权限受权、加密、会话治理等性能组合成了一个通用的平安认证框架. 构造subjectSubject(主体对象):负责提交用户认证和受权信息(用户/第三方服务) SecurityManagerSecurityManager(平安管理器):Shiro的外围,用来协调治理组件工作其蕴含组件有: Authenticator(认证管理器):负责执行认证操作。Authorizer(受权管理器):负责受权检测。SessionManager(会话治理):负责创立并治理用户 Session 生命周期,提供一个强有力的 Session 体验。SessionDAO:代表 SessionManager 执行 Session 长久(CRUD)动作,它容许任何存储的数据挂接到 session 治理根底上。CacheManager(缓存管理器):提供创立缓存实例和治理缓存生命周期的性能。Realms(畛域对象):是shiro和你的应用程序平安数据之间的桥梁CryptographyCryptography(加密管理器):提供了加密形式的设计及治理 官网中结构图如下所示: 认证拦挡实现增加依赖pom.xml中增加: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.3</version></dependency>实现1.创立一个shiro配置类,由@Configuration注解形容 package com.cy.pj.common.config;@Configurationpublic class SpringShiroConfig {}2.在Shiro配置类中增加SecurityManager配置(这里肯定要应用org.apache.shiro.mgt.SecurityManager这个接口对象)不要导错包 @Beanpublic SecurityManager securityManager() { DefaultWebSecurityManager sManager= new DefaultWebSecurityManager(); return sManager;}3.在Shiro配置类中增加ShiroFilterFactoryBean对象的配置。通过此对象设置资源匿名拜访、认证拜访 @Beanpublic ShiroFilterFactoryBean shiroFilterFactory (SecurityManager securityManager) { ShiroFilterFactoryBean sfBean= new ShiroFilterFactoryBean(); sfBean.setSecurityManager(securityManager); //定义map指定申请过滤规定(哪些资源容许匿名拜访,哪些必须认证拜访) LinkedHashMap<String,String> map= new LinkedHashMap<>(); //动态资源容许匿名拜访:"anon" map.put("/bower_components/**","anon"); map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); //除了匿名拜访的资源,其它都要认证("authc")后拜访 map.put("/**","authc"); sfBean.setFilterChainDefinitionMap(map); return sfBean;}4.在Controller层增加出现登陆页面的办法doLoginUI 5.批改SpringShiroConfig类中shiroFilterFactorybean的配置,增加登陆url的设置-- 标识 @Beanpublic ShiroFilterFactoryBean shiroFilterFactory (SecurityManager securityManager) { ShiroFilterFactoryBean sfBean= new ShiroFilterFactoryBean(); sfBean.setSecurityManager(securityManager); **sfBean.setLoginUrl("/doLoginUI")** //定义map指定申请过滤规定(哪些资源容许匿名拜访,哪些必须认证拜访) LinkedHashMap<String,String> map= new LinkedHashMap<>(); //动态资源容许匿名拜访:"anon" map.put("/bower_components/**","anon"); map.put("/build/**","anon"); map.put("/dist/**","anon"); map.put("/plugins/**","anon"); //除了匿名拜访的资源,其它都要认证("authc")后拜访 map.put("/**","authc"); sfBean.setFilterChainDefinitionMap(map); return sfBean;}认证业务流程身份认证即断定用户是否是零碎的非法用户,用户拜访系统资源时的认证(对用户身份信息的认证) ...

August 22, 2020 · 1 min · jiezi

springbootplus集成SpringBootShiroJWT权限管理

SpringBoot+Shiro+JWT权限管理ShiroApache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。三个核心组件:Subject, SecurityManager 和 Realms. Subject代表了当前用户的安全操作,即“当前操作用户”。SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。ShiroBasicArchitecture ShiroArchitecture JWTJSON Web Token(JWT)是目前最流行的跨域身份验证解决方案JSON Web令牌是一种开放的行业标准 RFC 7519方法,用于在双方之间安全地表示声明。JWT 数据结构eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJodHRwczovL3NwcmluZ2Jvb3QucGx1cyIsIm5hbWUiOiJzcHJpbmctYm9vdC1wbHVzIiwiaWF0IjoxNTE2MjM5MDIyfQ.1Cm7Ej8oIy1P5pkpu8-Q0B7bTU254I1og-ZukEe84II JWT有三部分组成:Header:头部,Payload:负载,Signature:签名SpringBoot+Shiro+JWTpom.xml Shiro依赖<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.4.1</version></dependency>pom.xml JWT依赖<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version></dependency>ShiroConfig.java配置@Slf4j@Configurationpublic class ShiroConfig { /** * JWT过滤器名称 */ private static final String JWT_FILTER_NAME = "jwtFilter"; /** * Shiro过滤器名称 */ private static final String SHIRO_FILTER_NAME = "shiroFilter"; @Bean public CredentialsMatcher credentialsMatcher() { return new JwtCredentialsMatcher(); } /** * JWT数据源验证 * * @return */ @Bean public JwtRealm jwtRealm(LoginRedisService loginRedisService) { JwtRealm jwtRealm = new JwtRealm(loginRedisService); jwtRealm.setCachingEnabled(false); jwtRealm.setCredentialsMatcher(credentialsMatcher()); return jwtRealm; } /** * 禁用session * * @return */ @Bean public DefaultSessionManager sessionManager() { DefaultSessionManager manager = new DefaultSessionManager(); manager.setSessionValidationSchedulerEnabled(false); return manager; } @Bean public SessionStorageEvaluator sessionStorageEvaluator() { DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator(); sessionStorageEvaluator.setSessionStorageEnabled(false); return sessionStorageEvaluator; } @Bean public DefaultSubjectDAO subjectDAO() { DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO(); defaultSubjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator()); return defaultSubjectDAO; } /** * 安全管理器配置 * * @return */ @Bean public DefaultWebSecurityManager securityManager(LoginRedisService loginRedisService) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(jwtRealm(loginRedisService)); securityManager.setSubjectDAO(subjectDAO()); securityManager.setSessionManager(sessionManager()); SecurityUtils.setSecurityManager(securityManager); return securityManager; } /** * ShiroFilterFactoryBean配置 * * @param securityManager * @param loginRedisService * @param shiroProperties * @param jwtProperties * @return */ @Bean(SHIRO_FILTER_NAME) public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, LoginService loginService, LoginRedisService loginRedisService, ShiroProperties shiroProperties, JwtProperties jwtProperties) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, Filter> filterMap = new HashedMap(); filterMap.put(JWT_FILTER_NAME, new JwtFilter(loginService, loginRedisService, jwtProperties)); shiroFilterFactoryBean.setFilters(filterMap); Map<String, String> filterChainMap = shiroFilterChainDefinition(shiroProperties).getFilterChainMap(); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap); return shiroFilterFactoryBean; } /** * Shiro路径权限配置 * * @return */ @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition(ShiroProperties shiroProperties) { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); // 获取ini格式配置 String definitions = shiroProperties.getFilterChainDefinitions(); if (StringUtils.isNotBlank(definitions)) { Map<String, String> section = IniUtil.parseIni(definitions); log.debug("definitions:{}", JSON.toJSONString(section)); for (Map.Entry<String, String> entry : section.entrySet()) { chainDefinition.addPathDefinition(entry.getKey(), entry.getValue()); } } // 获取自定义权限路径配置集合 List<ShiroPermissionConfig> permissionConfigs = shiroProperties.getPermissionConfig(); log.debug("permissionConfigs:{}", JSON.toJSONString(permissionConfigs)); if (CollectionUtils.isNotEmpty(permissionConfigs)) { for (ShiroPermissionConfig permissionConfig : permissionConfigs) { String url = permissionConfig.getUrl(); String[] urls = permissionConfig.getUrls(); String permission = permissionConfig.getPermission(); if (StringUtils.isBlank(url) && ArrayUtils.isEmpty(urls)) { throw new ShiroConfigException("shiro permission config 路径配置不能为空"); } if (StringUtils.isBlank(permission)) { throw new ShiroConfigException("shiro permission config permission不能为空"); } if (StringUtils.isNotBlank(url)) { chainDefinition.addPathDefinition(url, permission); } if (ArrayUtils.isNotEmpty(urls)) { for (String string : urls) { chainDefinition.addPathDefinition(string, permission); } } } } // 最后一个设置为JWTFilter chainDefinition.addPathDefinition("/**", JWT_FILTER_NAME); Map<String, String> filterChainMap = chainDefinition.getFilterChainMap(); log.debug("filterChainMap:{}", JSON.toJSONString(filterChainMap)); return chainDefinition; } /** * ShiroFilter配置 * * @return */ @Bean public FilterRegistrationBean delegatingFilterProxy() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); DelegatingFilterProxy proxy = new DelegatingFilterProxy(); proxy.setTargetFilterLifecycle(true); proxy.setTargetBeanName(SHIRO_FILTER_NAME); filterRegistrationBean.setFilter(proxy); filterRegistrationBean.setAsyncSupported(true); filterRegistrationBean.setEnabled(true); filterRegistrationBean.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC); return filterRegistrationBean; } @Bean public Authenticator authenticator(LoginRedisService loginRedisService) { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); authenticator.setRealms(Arrays.asList(jwtRealm(loginRedisService))); authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy()); return authenticator; } /** * Enabling Shiro Annotations * * @return */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * depends-on lifecycleBeanPostProcessor * * @return */ @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); return defaultAdvisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; }}JWT过滤器配置@Slf4jpublic class JwtFilter extends AuthenticatingFilter { private LoginService loginService; private LoginRedisService loginRedisService; private JwtProperties jwtProperties; public JwtFilter(LoginService loginService, LoginRedisService loginRedisService, JwtProperties jwtProperties) { this.loginService = loginService; this.loginRedisService = loginRedisService; this.jwtProperties = jwtProperties; } /** * 将JWT Token包装成AuthenticationToken * * @param servletRequest * @param servletResponse * @return * @throws Exception */ @Override protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception { String token = JwtTokenUtil.getToken(); if (StringUtils.isBlank(token)) { throw new AuthenticationException("token不能为空"); } if (JwtUtil.isExpired(token)) { throw new AuthenticationException("JWT Token已过期,token:" + token); } // 如果开启redis二次校验,或者设置为单个用户token登陆,则先在redis中判断token是否存在 if (jwtProperties.isRedisCheck() || jwtProperties.isSingleLogin()) { boolean redisExpired = loginRedisService.exists(token); if (!redisExpired) { throw new AuthenticationException("Redis Token不存在,token:" + token); } } String username = JwtUtil.getUsername(token); String salt; if (jwtProperties.isSaltCheck()){ salt = loginRedisService.getSalt(username); }else{ salt = jwtProperties.getSecret(); } return JwtToken.build(token, username, salt, jwtProperties.getExpireSecond()); } /** * 访问失败处理 * * @param request * @param response * @return * @throws Exception */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { HttpServletRequest httpServletRequest = WebUtils.toHttp(request); HttpServletResponse httpServletResponse = WebUtils.toHttp(response); // 返回401 httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 设置响应码为401或者直接输出消息 String url = httpServletRequest.getRequestURI(); log.error("onAccessDenied url:{}", url); ApiResult apiResult = ApiResult.fail(ApiCode.UNAUTHORIZED); HttpServletResponseUtil.printJSON(httpServletResponse, apiResult); return false; } /** * 判断是否允许访问 * * @param request * @param response * @param mappedValue * @return */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { String url = WebUtils.toHttp(request).getRequestURI(); log.debug("isAccessAllowed url:{}", url); if (this.isLoginRequest(request, response)) { return true; } boolean allowed = false; try { allowed = executeLogin(request, response); } catch (IllegalStateException e) { //not found any token log.error("Token不能为空", e); } catch (Exception e) { log.error("访问错误", e); } return allowed || super.isPermissive(mappedValue); } /** * 登陆成功处理 * * @param token * @param subject * @param request * @param response * @return * @throws Exception */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { String url = WebUtils.toHttp(request).getRequestURI(); log.debug("鉴权成功,token:{},url:{}", token, url); // 刷新token JwtToken jwtToken = (JwtToken) token; HttpServletResponse httpServletResponse = WebUtils.toHttp(response); loginService.refreshToken(jwtToken, httpServletResponse); return true; } /** * 登陆失败处理 * * @param token * @param e * @param request * @param response * @return */ @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { log.error("登陆失败,token:" + token + ",error:" + e.getMessage(), e); return false; }}JWT Realm配置@Slf4jpublic class JwtRealm extends AuthorizingRealm { private LoginRedisService loginRedisService; public JwtRealm(LoginRedisService loginRedisService) { this.loginRedisService = loginRedisService; } @Override public boolean supports(AuthenticationToken token) { return token != null && token instanceof JwtToken; } /** * 授权认证,设置角色/权限信息 * * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { log.debug("doGetAuthorizationInfo principalCollection..."); // 设置角色/权限信息 String token = principalCollection.toString(); // 获取username String username = JwtUtil.getUsername(token); // 获取登陆用户角色权限信息 LoginSysUserRedisVo loginSysUserRedisVo = loginRedisService.getLoginSysUserRedisVo(username); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 设置角色 authorizationInfo.setRoles(loginSysUserRedisVo.getRoles()); // 设置权限 authorizationInfo.setStringPermissions(loginSysUserRedisVo.getPermissions()); return authorizationInfo; } /** * 登陆认证 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { log.debug("doGetAuthenticationInfo authenticationToken..."); // 校验token JwtToken jwtToken = (JwtToken) authenticationToken; if (jwtToken == null) { throw new AuthenticationException("jwtToken不能为空"); } String salt = jwtToken.getSalt(); if (StringUtils.isBlank(salt)) { throw new AuthenticationException("salt不能为空"); } return new SimpleAuthenticationInfo( jwtToken, salt, getName() ); }}更多配置:https://github.com/geekidea/spring-boot-plusapplication.yml配置############################## spring-boot-plus start ##############################spring-boot-plus: ######################## Spring Shiro start ######################## shiro: # shiro ini 多行字符串配置 filter-chain-definitions: | /=anon /static/**=anon /templates/**=anon # 权限配置 permission-config: # 排除登陆登出相关 - urls: /login,/logout permission: anon # 排除静态资源 - urls: /static/**,/templates/** permission: anon # 排除Swagger - urls: /docs,/swagger-ui.html, /webjars/springfox-swagger-ui/**,/swagger-resources/**,/v2/api-docs permission: anon # 排除SpringBootAdmin - urls: /,/favicon.ico,/actuator/**,/instances/**,/assets/**,/sba-settings.js,/applications/** permission: anon # 测试 - url: /sysUser/getPageList permission: anon ######################## Spring Shiro end ########################## ############################ JWT start ############################# jwt: token-name: token secret: 666666 issuer: spring-boot-plus audience: web # 默认过期时间1小时,单位:秒 expire-second: 3600 # 是否刷新token refresh-token: true # 刷新token的时间间隔,默认10分钟,单位:秒 refresh-token-countdown: 600 # redis校验jwt token是否存在,可选 redis-check: true # true: 同一个账号只能是最后一次登陆token有效,false:同一个账号可多次登陆 single-login: false # 盐值校验,如果不加自定义盐值,则使用secret校验 salt-check: true ############################ JWT end ############################################################## spring-boot-plus end ###############################Redis存储信息使用Redis缓存JWTToken和盐值:方便鉴权,token后台过期控制等Redis二次校验和盐值校验是可选的127.0.0.1:6379> keys *1) "login:user:token:admin:0f2c5d670f9f5b00201c78293304b5b5"2) "login:salt:admin"3) "login:user:admin"4) "login:token:0f2c5d670f9f5b00201c78293304b5b5"Redis存储的JwtToken信息127.0.0.1:6379> get login:token:0f2c5d670f9f5b00201c78293304b5b5{ "@class": "io.geekidea.springbootplus.shiro.vo.JwtTokenRedisVo", "host": "127.0.0.1", "username": "admin", "salt": "f80b2eed0110a7ea5a94c35cbea1fe003d9bb450803473428b74862cceb697f8", "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJ3ZWIiLCJpc3MiOiJzcHJpbmctYm9vdC1wbHVzIiwiZXhwIjoxNTcwMzU3ODY1LCJpYXQiOjE1NzAzNTQyNjUsImp0aSI6IjE2MWQ1MDQxZmUwZjRmYTBhOThjYmQ0ZjRlNDI1ZGQ3IiwidXNlcm5hbWUiOiJhZG1pbiJ9.0ExWSiniq7ThMXfqCOi9pCdonY8D1azeu78_vLNa2v0", "createDate": [ "java.util.Date", 1570354265000 ], "expireSecond": 3600, "expireDate": [ "java.util.Date", 1570357865000 ]}ReferenceShirohttps://shiro.apache.org/spring.htmlhttps://shiro.apache.org/spring-boot.htmlJWThttps://jwt.io/https://github.com/auth0/java-jwtspring-boot-plushttps://github.com/geekidea/spring-boot-plushttps://springboot.plus/guide/shiro-jwt.html

October 8, 2019 · 6 min · jiezi

Spring-Boot-整合-Shiro-两种方式全总结

在 Spring Boot 中做权限管理,一般来说,主流的方案是 Spring Security ,但是,仅仅从技术角度来说,也可以使用 Shiro。 <!--more--> 今天松哥就来和大家聊聊 Spring Boot 整合 Shiro 的话题! 一般来说,Spring Security 和 Shiro 的比较如下: Spring Security 是一个重量级的安全管理框架;Shiro 则是一个轻量级的安全管理框架Spring Security 概念复杂,配置繁琐;Shiro 概念简单、配置简单Spring Security 功能强大;Shiro 功能简单...虽然 Shiro 功能简单,但是也能满足大部分的业务场景。所以在传统的 SSM 项目中,一般来说,可以整合 Shiro。 在 Spring Boot 中,由于 Spring Boot 官方提供了大量的非常方便的开箱即用的 Starter ,当然也提供了 Spring Security 的 Starter ,使得在 Spring Boot 中使用 Spring Security 变得更加容易,甚至只需要添加一个依赖就可以保护所有的接口,所以,如果是 Spring Boot 项目,一般选择 Spring Security 。 这只是一个建议的组合,单纯从技术上来说,无论怎么组合,都是没有问题的。 在 Spring Boot 中整合 Shiro ,有两种不同的方案: ...

June 11, 2019 · 2 min · jiezi

springboot 集成 shiro 导致事务无效

问题描述前两天测试一个写事务,发现这个事务出现异常不会回滚了,一直在事务上找问题,一直没有找到,结果发现是shiro的bean先于Spring事务将userService实例化了,结果导致spring事务初始化时好无法扫描到该bean,导致这个bean上没有绑定事务,导致事务无效寻找问题在哪一、在事务本身找问题通过百度发现,大家都有以下几个原因导致事务失效数据库的引擎是否是innoDB 启动类上是否加入@EnableTransactionManagement注解 方法是否为public是否是因为抛出了Exception等checked异常经过排查,发现以上原因都通过了,那么应该不是写的问题。二、在运行中找问题在上面4个原因检查时,发现将已有的service 类 copy下现在有两个除了名字其他都一模一样的类,这时运行下发现,在原来的类中@Transational失效,在新copy中的类中@Transational就起效了,这个问题好莫名奇妙,什么都没改就一个有效一个无效,现在的思路就是比较下这两个类在运行时有什么不同通过log发现打出了一下信息,说是jdbc的connection 不是Spring管的而正常回归的service类则是,调用了 JtaTransactionManager 类,而且 spring是管理jdbc的connection的通过这个分析,可以知道这spring对于这两个类的处理是不一样的,应该是spring代理或者初始化的问题,翻了下log 发现service 在ProxyTransactionManagementConfiguration 配置之前就被创建了,那应该是这里的问题了,这里就要分析下service为啥提前被创建了,发现在开始启动的是shiro ,而shiro中有个realm中引用了这些服务,所以这些服务在Transaction创建扫描之前创建了引发问题原因总结导致问题的真正原因是bean创建顺序问题,解决问题方法就是,在Transaction之后创建service。ps:呵呵,但是我还是不知道咋样才能解决创建顺序问题,继续百度之,关键词shiro 导致 事务不生效果然有解决方案解决方案经过百度找到了以下的解决方法,和以下解释shiro导致springboot事务不起效解决办法BeanPostProcessor加载次序及其对Bean造成的影响分析spring boot shiro 事务无效Shrio 多realms集成:No realms have been configured! One or more realms must be presentspring + shiro 配置中部分事务失效分析及解决方案(这个方案不管用)解决方法一:在realm引用的service服务上加@lazy注解,但是这个方法我测试了下并没有起效!!!解决方法二:把在 ShiroConfig里面初始化的Realm的bean和securityManager的bean方法移动到一个新建的ShiroComponent中,利用监听器中去初始化,主要配置如下,其中ShiroComponent中UserNamePassWordRealm和WeiXinRealm是我自定义的两个Realm,换成自己的就好,ShiroConfig.javaimport java.util.LinkedHashMap;import java.util.Map;import javax.servlet.DispatcherType;import javax.servlet.Filter;import org.apache.shiro.cache.ehcache.EhCacheManager;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.filter.DelegatingFilterProxy;/** * 自定义继承shiro 没有使用shiro-spring-boot-web-starter 的shiro 套件 * * @author gaoxiuya * /@Configurationpublic class ShiroConfig { /* * FilterRegistrationBean * * @return / @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistration = new FilterRegistrationBean(); filterRegistration.setFilter(new DelegatingFilterProxy(“shiroFilter”)); filterRegistration.setEnabled(true); filterRegistration.addUrlPatterns("/"); filterRegistration.setDispatcherTypes(DispatcherType.REQUEST); return filterRegistration; } /** * @param securityManager * @see org.apache.shiro.spring.web.ShiroFilterFactorupload.visit.pathyBean * @return / @Bean(name = “shiroFilter”) public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager); bean.setLoginUrl("/"); bean.setSuccessUrl("/index"); bean.setUnauthorizedUrl("/403"); Map<String, Filter> filters = new LinkedHashMap<>(); filters.put(“permsc”, new CustomPermissionsAuthorizationFilter()); bean.setFilters(filters); Map<String, String> chains = new LinkedHashMap<>(); chains.put("/favicon.ico", “anon”); bean.setFilterChainDefinitionMap(chains); return bean; } @Bean public EhCacheManager cacheManager() { EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile(“classpath:ehcache.xml”); return cacheManager; } /* * @see DefaultWebSessionManager * @return */ @Bean(name = “sessionManager”) public DefaultWebSessionManager defaultWebSessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setCacheManager(cacheManager()); sessionManager.setGlobalSessionTimeout(1800000); sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setDeleteInvalidSessions(true); return sessionManager; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); }}ShiroComponent.javaimport java.util.ArrayList;import java.util.List;import org.apache.shiro.authc.Authenticator;import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;import org.apache.shiro.authc.pam.ModularRealmAuthenticator;import org.apache.shiro.cache.CacheManager;import org.apache.shiro.realm.Realm;import org.apache.shiro.session.mgt.SessionManager;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.event.ContextRefreshedEvent;import org.springframework.context.event.EventListener;import org.springframework.stereotype.Component;@Componentpublic class ShiroComponent { @Bean public Realm userNamePassWordRealm(CacheManager cacheManager) { UserNamePassWordRealm userNamePassWordRealm = new UserNamePassWordRealm(); userNamePassWordRealm.setCacheManager(cacheManager); return userNamePassWordRealm; } @Bean public Realm myWeiXinRealm(CacheManager cacheManager) { WeiXinRealm weiXinRealm = new WeiXinRealm(); weiXinRealm.setCacheManager(cacheManager); return weiXinRealm; } @Bean(name = “securityManager”) public DefaultWebSecurityManager securityManager(Authenticator modularRealmAuthenticator, CacheManager cacheManager, SessionManager defaultWebSessionManager) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setAuthenticator(modularRealmAuthenticator); manager.setCacheManager(cacheManager); manager.setSessionManager(defaultWebSessionManager); return manager; } @Bean public Authenticator modularRealmAuthenticator() { ModularRealmAuthenticator modularRealmAuthenticator = new ModularRealmAuthenticator(); modularRealmAuthenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy()); return modularRealmAuthenticator; } @EventListener public void handleContextRefresh(ContextRefreshedEvent event) { ApplicationContext context = event.getApplicationContext(); DefaultWebSecurityManager manager = (DefaultWebSecurityManager) context.getBean(“securityManager”); Realm userNamePassWordRealm = (Realm) context.getBean(“userNamePassWordRealm”); Realm myWeiXinRealm = (Realm) context.getBean(“myWeiXinRealm”); ModularRealmAuthenticator modularRealmAuthenticator = (ModularRealmAuthenticator) context .getBean(“modularRealmAuthenticator”); List<Realm> realms = new ArrayList<>(); realms.add(userNamePassWordRealm); realms.add(myWeiXinRealm); modularRealmAuthenticator.setRealms(realms); manager.setAuthenticator(modularRealmAuthenticator); manager.setRealms(realms); }}总结以后需要补课的地方spring bean 初始化顺序spring 事务原理spring bean 预加载 BeanPostProces 原理@Lazy 原理和为啥不起效 ...

April 7, 2019 · 2 min · jiezi

SpringBoot 填坑 | Shiro 与 Redis 多级缓存问题

微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。前言来自不愿意透露姓名的小师弟的投稿。这篇主要讲了,项目中配置了多缓存遇到的坑,以及解决办法。发现问题在一次项目实践中有实现多级缓存其中有已经包括了 Shiro 的 Cache ,本以为开启 redis 的缓存是一件很简单的事情只需要在启动类上加上 @EnableCaching 注解就会启动缓存管理了,但是问题出现了。重要错误日志截图java.lang.IllegalStateException: @Bean method ShiroConfig.cacheManager called as a bean reference for type [org.apache.shiro.cache.ehcache.EhCacheManager] but overridden by non-compatible bean instance of type [org.springframework.data.redis.cache.RedisCacheManager]. Overriding bean of same name declared in: class path resource [org/springframework/boot/autoconfigure/cache/RedisCacheConfiguration.class]错误日志分析看日志大概就发现一个非法状态异常,我们继续查看接下来的日志有一段非常的重要日志 Overriding bean of same name 翻译过来的意思是帮你重写了一个名字一样的 Bean,我再看看日志里有提到 RedisCacheManager 与我自己实现的 cacheManager 到这里我已经感觉到问题所在了,以下图一为 RedisCacheManager 部分实现代码。图二为我自己的 Shiro 的 cacheManager 实现方法。解决问题有 Spring 基础的大家都应该还记得 Spring 不允许有相同的 Bean 出现。现在问题就在于 Redis 缓存管理器和 Shiro 的缓存管理器重名了,而这二者又是通过 Spring 管理,所以 Spring 读取这二者的时候,产生冲突了。解决问题的方法很简单:在自己实现 EhCacheManager 时把 @Bean 指定一个名字可以像这样 @Bean(name =“ehCacheManager” ),还有其他办法大家可以在想办法实现一下嘿嘿。结语虽然我们都知道 Spring 的报错是非常多的,但是在 Spring 的报错日志中查找问题所在是非常有用的,大部分的错误,日志都会给你反馈。如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。另外,关注之后在发送 1024 可领取免费学习资料。资料详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

March 16, 2019 · 1 min · jiezi

OA管理系统 - SpringBoot + AmazeUi

本项目由 zzzmh & csyd 共同开发完成zzzmh : https://zzzmh.cn/csyd : http://csyd.xyz/前端: Amazeui + vue后端: Springboot + shiro + redis & Mysql截图展示登录首页人员信息系统监控模块系统日志建议去在线浏览一下在线浏览地址https://zzzmh.cn/projectoa/indexGitHub地址https://github.com/1812125969…Gitee码云地址https://gitee.com/tczmh/proje…DB设计书http://leanote.com/blog/post/…环境 & 框架JDK 1.8Tomcat 8.5IntelliJ IDEA 2017.3SpringBoot 1.5.10Mybatis-spring-boot-starter 1.3.1Mysql 5.7Gradle 4.4插件 & 工具Logback (spring内置) 用于记录日志文件Spring Data Redis (spring内置) 用户缓存Spring Boot ShiroSpring Boot Admin前端使用的模板AmazeUI 2.7.2prism 用于<pre>标签的代码高亮Font Awesome (amaze ui内置) 用于显示统一图标FullCalendar (amaze ui内置) 一个带备注的日历插件GitHub地址https://github.com/1812125969…其他的一些相关笔记链接毕业设计-开发避坑指南!http://leanote.com/blog/post/…DB设计http://leanote.com/blog/post/…shiro-springhttp://leanote.com/blog/post/…Spring-Data-Redishttp://leanote.com/blog/post/…彩虹猫 banner.txt for Spring Boothttp://leanote.com/blog/post/…参考http://www.ityouknow.com/spri…另外本文也发到了我的个人博客 https://zzzmh.cn/single?id=2END

January 23, 2019 · 1 min · jiezi