🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] ## 1. 创建SpringBoot项目(整合mybatis) ### 1.1 新建springboot项目 > 1. 新建项目【File】>【New】>【Project】 ![](https://box.kancloud.cn/1def4fc75d9ef8e4ecb96d7a380fd8df_704x662.png) > 2. 选择【Spring Initializer】> 【next】 ![](https://box.kancloud.cn/8414606ffe29fe2ae510011fd42b9dec_828x812.png) > 3. 填写项目信息 ![](https://box.kancloud.cn/27bc173606dfd1b3758c615908565382_828x812.png) > 4. 加入web,mybatis,mysql 【NEXT】 ![](https://box.kancloud.cn/f78d7105f87218a75404887fcef3512d_826x796.png) ![](https://box.kancloud.cn/dec9ecfc7864d0a28cebbf8c372d0c5e_828x812.png) > 5. finish > 这样intellij会自动为我们引入web、mybatis所需要的依赖 ### 1.2 根据表自动生成model,mapper(可选) 使用mybatis-generator根据数据库表,自动生成model和mapper代码 #### 1.2.1 修改pom.xml文件,指定生成代码的保存位置 > 记得新建: > net.aexit.infrastructure.common.mapper > net.aexit.infrastructure.common.model > net.aexit.infrastructure.common.mapper ~~~ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- MyBatis Generator --> <!-- Java接口和实体类,自动生成的存放位置 --> <targetJavaProject>${basedir}/src/main/java</targetJavaProject> <targetXmlProject>${basedir}/src/main/java</targetXmlProject> <targetMapperPackage>net.aexit.infrastructure.common.mapper</targetMapperPackage> <targetModelPackage>net.aexit.infrastructure.common.model</targetModelPackage> <targetXMLPackage>net.aexit.infrastructure.common.mapper</targetXMLPackage> </properties> ~~~ #### 1.2.2 建立文件夹generator,放入jar文件 ![](https://box.kancloud.cn/083f58076397ca5d256c55170c4ba0b8_456x113.png) jar链接:https://pan.baidu.com/s/1qZnlxoS 密码:fcey #### 1.2.3 maven配置pom.xml书写 mybatis-generator-maven-plugin ~~~ <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.2</version> <configuration> <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> <verbose>true</verbose> </configuration> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency> <dependency> <groupId>com.xxg</groupId> <artifactId>mybatis-generator-plugin</artifactId> <version>1.0.0</version> <scope>system</scope> <systemPath>${basedir}/src/main/resources/generator/mybatis-generator-plugin-1.0.0.jar</systemPath> </dependency> </dependencies> </plugin> </plugins> <resources> <resource> <directory>src/main/java</directory> <filtering>false</filtering> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> </resource> </resources> </build> ~~~ #### 1.2.4 在generator文件夹下编写generatorConfig.xml文件,固定格式 ~~~ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!--此文件用于从数据库表结构,自动生成model,自动生成基础CURD的mapper代码 mvn mybatis-generator:generate--> <!--<properties resource="./generator/generatorConfig.properties"/>--> <!-- 数据库驱动包位置,使用maven的方法,这个就不用了 --> <context id="MYSQLTables" targetRuntime="MyBatis3" defaultModelType="flat"> <!-- 生成的Java文件的编码 --> <property name="javaFileEncoding" value="UTF-8"/> <!-- 格式化java代码 --> <property name="javaFormatter" value="org.mybatis.generator.api.dom.DefaultJavaFormatter"/> <!-- 格式化XML代码 --> <property name="xmlFormatter" value="org.mybatis.generator.api.dom.DefaultXmlFormatter"/> <plugin type="com.xxg.mybatis.plugins.MySQLLimitPlugin"></plugin> <commentGenerator> <property name="suppressAllComments" value="true" /> </commentGenerator> <!-- 数据库链接URL、用户名、密码 --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://192.168.x.xx:3306/test?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true" userId="test" password="123456"> </jdbcConnection> <javaModelGenerator targetPackage="${targetModelPackage}" targetProject="${targetJavaProject}"/> <sqlMapGenerator targetPackage="${targetXMLPackage}" targetProject="${targetXmlProject}"/> <javaClientGenerator targetPackage="${targetMapperPackage}" targetProject="${targetJavaProject}" type="XMLMAPPER" /> <!-- mvn mybatis-generator:generate --> <table tableName="sys_user"/> </context> </generatorConfiguration> ~~~ #### 1.2.5 命令行生成代码 > 在pom.xml的同级目录执行 ~~~ mvn mybatis-generator:generate ~~~ #### 1.2.6 查看生成结果 可以看到自动生成了四个文件 > 1. SysUserMapper.java > 2. SysUserMapper.xml > 3. SysUser的实体类 > 4. SysUserExample,单表复杂数据库操作模板 有了以上的代码,针对单表的操作接近100%,不用写SQL!具体如何用好这四个类,后文详细讲解! 注意:一旦数据库表结构发生变化,需要重新生成4个文件!请先删除xml,如:SysUserMapper.xml ,因为追加不覆盖导致报错, **会根据驼峰规则生成对象 ** role_id字段对应roleId属性; role_name对应roleName; **类依然是首字母大写** ![](https://box.kancloud.cn/0f7609b832be1f53ebd8e994f810ae9d_1699x517.png) #### 1.2.7 如何获取insert的自增key. 配置,自动生成的代码是没有useGeneratedKeys="true" keyProperty="id"这个配置的,需要加上. ~~~ <insert id="insert" parameterType="net.aexit.infrastructure.common.model.SysUser" useGeneratedKeys="true" keyProperty="id"> insert into sys_user (id, user_id, user_name, password, salt, org_id, phone, address, sex, email, remarks, create_date, head_image, status) values (#{id,jdbcType=INTEGER}, #{userId,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{salt,jdbcType=VARCHAR}, #{orgId,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR}, #{sex,jdbcType=TINYINT}, #{email,jdbcType=VARCHAR}, #{remarks,jdbcType=VARCHAR}, #{createDate,jdbcType=TIMESTAMP}, #{headImage,jdbcType=VARCHAR}, #{status,jdbcType=TINYINT}) </insert> ~~~ • 获取 ~~~ this.sysUserMapper.insert(sysUser); return sysUser.getId(); ~~~ 这样就生成了model,和mapper ![](https://box.kancloud.cn/dd9e23782b9439a925e13bc7ef3cc3c6_638x333.png) ### 1.3 测试mybatis 1. 设置扫描 ~~~ @SpringBootApplication @ComponentScan(basePackages = {"com.aixin"}) public class SpringbootApplication { public static void main(String[] args) { SpringApplication.run(SpringbootApplication.class, args); } } ~~~ 2. 扫描mapper ~~~ package com.aixin.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * Created by dailin on 2018/1/9. */ @Configuration @MapperScan("com.**.mapper") public class MybatisConfig { } ~~~ 3. mapper添加方法 ~~~ Userinfo findByUsername(String userName); ~~~ 4. 配置xml添加SQL ~~~ <select id="findByUsername" parameterType="string" resultMap="BaseResultMap"> SELECT * FROM UserInfo WHERE username=#{userName} </select> ~~~ 5. service 接口 ~~~ package com.tuna.service; import com.tuna.model.Userinfo; public interface UserInfoService { /**通过username查找用户信息;*/ public Userinfo findByUsername(String username); } ~~~ 实现类 ~~~ package com.tuna.service; import javax.annotation.Resource; import com.tuna.mapper.UserinfoMapper; import com.tuna.model.Userinfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class UserInfoServiceImpl implements UserInfoService{ @Autowired private UserinfoMapper userinfoMapper; @Override public Userinfo findByUsername(String username) { System.out.println("UserInfoServiceImpl.findByUsername()"); return userinfoMapper.findByUsername(username); } } ~~~ 6. controller ~~~ package com.tuna.controller; import com.tuna.model.Userinfo; import com.tuna.service.UserInfoService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import javax.websocket.server.PathParam; /** * Created by dailin on 2018/1/9. */ @RestController public class Hello { @Autowired private UserInfoService userInfoService; @RequestMapping("/hello") public Userinfo sayHello(){ Userinfo userInfo = userInfoService.findByUsername("admin"); return userInfo; } } ~~~ 7. application.yml 配置 ~~~ server: port: 8090 address: 0.0.0.0 session: timeout: 28800 ################################################# 数据库访问配置 # 主数据源,默认的 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.56.130:3306/shiro?useUnicode=true&characterEncoding=utf8&autoReconnect=true username: root password: tuna mybatis-plus: configuration: mapUnderscoreToCamelCase: true ~~~ 8. 结果 ![](https://box.kancloud.cn/7e859e48f575b306c8a7c55407ac0531_841x391.png) ## 2. 整合实例 上面创建项目时,自动完成了mybatis的整合 ### 2.1 集成druid 1.添加maven依赖 ~~~ <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.2</version> </dependency> ~~~ 2. 配置数据源application.yml 数据库访问配置 ~~~ # 主数据源,默认的 datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.2.21:3306/test?useUnicode=true&characterEncoding=utf8&autoReconnect=true username: test password: 123456 druid: initialSize: 2 minIdle: 2 maxActive: 30 WebStatFilter: exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*' StatViewServlet: loginUsername: aexit loginPassword: aexit ~~~ 3.查看监控,输入用户名密码,上文 访问:http://localhost:20001/druid/ ![](https://box.kancloud.cn/2b37d39866e2681c8fe8bafcac65b6e2_495x285.png) ### 2.2 整合beetl 1. 引入maven ~~~ <dependency> <groupId>com.ibeetl</groupId> <artifactId>beetl</artifactId> <version>2.7.21</version> </dependency> ~~~ * beetl提供了starter的方式集成springboot,但是经过试验,和druid有冲突!因为beetl-starter不但集成了模板,还集成了beetlSQL,和druid数据库连接池发生冲突! * 所以不引入带starter的maven包! 2. 配置beetl基础信息 ~~~ package com.aixin.tuna.webtest.config; import org.beetl.core.resource.ClasspathResourceLoader; import org.beetl.ext.spring.BeetlGroupUtilConfiguration; import org.beetl.ext.spring.BeetlSpringViewResolver; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Properties; @Configuration public class BeetlTplConfig extends BeetlGroupUtilConfiguration { @Bean(initMethod = "init", name = "beetlConfig") public BeetlGroupUtilConfiguration getBeetlGroupUtilConfiguration() { BeetlGroupUtilConfiguration beetlGroupUtilConfiguration = new BeetlGroupUtilConfiguration(); ClasspathResourceLoader classpathResourceLoader = new ClasspathResourceLoader(); beetlGroupUtilConfiguration.setResourceLoader(classpathResourceLoader); Properties beetlConfigProperties = new Properties(); //是否检测文件变化,开发用true合适,但线上要改为false beetlConfigProperties.setProperty("RESOURCE.autoCheck","true"); //自定义标签文件Root目录和后缀 beetlConfigProperties.setProperty("RESOURCE.tagRoot","templates/tags"); beetlConfigProperties.setProperty("RESOURCE.tagSuffix","tag"); beetlGroupUtilConfiguration.setConfigProperties(beetlConfigProperties); return beetlGroupUtilConfiguration; } @Bean(name = "beetlViewResolver") public BeetlSpringViewResolver getBeetlSpringViewResolver(@Qualifier("beetlConfig") BeetlGroupUtilConfiguration beetlGroupUtilConfiguration) { BeetlSpringViewResolver beetlSpringViewResolver = new BeetlSpringViewResolver(); beetlSpringViewResolver.setPrefix("/templates/"); beetlSpringViewResolver.setSuffix(".html"); beetlSpringViewResolver.setContentType("text/html;charset=UTF-8"); beetlSpringViewResolver.setOrder(0); beetlSpringViewResolver.setConfig(beetlGroupUtilConfiguration); return beetlSpringViewResolver; } @Override public void initOther() { // groupTemplate.registerFunctionPackage("shiro", new ShiroKit()); // groupTemplate.registerFunctionPackage("tool", new ToolUtil()); } } ~~~ 例子 ~~~ 控制层 @Controller public class LoginController { @RequestMapping(value = "/", method = RequestMethod.GET) public String index(Model model) { model.addAttribute("name", "代林"); return "login"; } } ~~~ 显示层,放在resources/templates目录下面的login.html ~~~ <html lang="en"> <head> <title>beetl servlet</title> </head> <body> 这是${name}的第一个beetl例子 </body> </html> ~~~ 访问 ![](https://box.kancloud.cn/3a49a56daaf18561dfdbbd9a122b1a1e_1174x187.png) ### 2.3 整合shrio #### 2.3.1 引入shrio依赖 <shiro.version>1.4.0</shiro.version> ~~~ <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> ~~~ #### 2.3.2 原理讲解 > ShiroConfiguration.java: > shiro启动时候的初始化工作,比如哪些是需要认证,哪些不需要认证;缓存配置设置;shiro权限数据在页面展示时整合需要的模板套件配置,等等。 > ShiroRealm.java: > shiro权限认证的具体实现代码,因为shiro本身只提供拦截路由,而具体如何数据源则由用户自己提供,不同的项目不同的要求,要不要加缓存登陆验证次数,要不要密码加密设置其他具体方式,这些都由用户自己决定,而shiro只提供给用户权限验证的格式接口,通过用户提供的数据源shrio判断要不要给具体用户授权请求路径的判断。 > ShiroRealm 涉及到以下点: > principal: > 主体,就是登陆的当前用户类型的数据实体 > credentials: > 凭证,用户的密码,具体加密方式用户自己实现,什么都不做就是原文 > Roles: > 用户拥有的角色标识(角色名称,admin,account,customer_service),字符串格式列表:用户拥有多个角色的可能 > Permissions: > 用户拥有的权限标识(每个权限唯一标识,比如主键或者权限唯一标识编码),字符串格式列表:用户拥有多个权限的可能 #### 2.3.3 ShrioRealm 1. 用户登陆信息及权限配置 ~~~ @Component("aexitShrioRealm") public class AexitShrioRealm extends AuthorizingRealm { @Resource UserService userService; @Resource RoleService roleService; @Resource MenuService menuService; /** * 配置用户权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { throw new AuthorizationException("PrincipalCollection method argument cannot be null."); } String userId = (String) getAvailablePrincipal(principals); List<String> roleList = roleService.getRoleIdsByUserId(userId); //用户角色获取用户功能id Set<String> roleSet = new HashSet<>(); //角色集合 Set<String> menuSet = new HashSet<>(); //菜单集合 List<String> menus; for(String roleId : roleList){ roleSet.add(roleId); menus = menuService.getMenuidsByRoleId(roleId); Collections.addAll(menuSet,menus.toArray(new String[menus.size()])); } SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(roleSet); authorizationInfo.setStringPermissions(menuSet); return authorizationInfo; } /** * 配置登录认证信息 * * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String userId = token.getUsername(); if (userId == null) { throw new AccountException("Null usernames are not allowed by this realm."); } //查出是否有此用户 SysUser curUser = userService.getByUserId(userId); if(curUser == null) throw new AccountException("account error:one user name must have one and only one user! "); //密码加密 String password = ShiroKit.md5(curUser.getPassword(),curUser.getSalt()); return new SimpleAuthenticationInfo(userId, password, getName()); } } ~~~ 2. 用户登陆验证 ~~~ /** * 登陆验证 * @return */ @RequestMapping(value="/login",method = RequestMethod.POST) @ResponseBody public AjaxCommonObject login(@RequestParam(value="user_id",required = false) String userId, @RequestParam(value="password",required = false) String password, @RequestParam(value="user_validate_code",required = false) String userValidateCode, HttpSession session) { AjaxCommonObject ajaxCommonObject = new AjaxCommonObject(); try { if (!userValidateCode.equals(session.getAttribute(Constants.KAPTCHA_SESSION_KEY))) { throw new BizCommonException(400,"请输入正确的验证码!"); } SysUser user = userService.getByUserId(userId.trim()); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken( userId, ShiroKit.md5(password,user.getSalt())); subject.login(token); //登陆成功之后 session.setAttribute(AuthConstants.CURRENT_USER, user); session.setAttribute(AuthConstants.DICTIONRYS, sysDictionaryMapper.selectByExample(new SysDictionaryExample()));//数据字典 } catch (BizCommonException e) { return new AjaxCommonObject(new BizCommonException(400,"请输入正确的用户名和密码!")); } return ajaxCommonObject; } ~~~ 3. shiro生命周期的管理配置 ~~~ /** * shiro生命周期配置项 * Created by hanxt on 2017/12/18. */ @Configuration public class ShiroConfiguration { @Resource SysMenuExtMapper sysMenuExtMapper; @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean(name = "shiroRealm") @DependsOn("lifecycleBeanPostProcessor") public AexitShrioRealm shiroRealm() { AexitShrioRealm realm = new AexitShrioRealm(); return realm; } @Bean(name = "ehCacheManager") @DependsOn("lifecycleBeanPostProcessor") public EhCacheManager ehCacheManager() { EhCacheManager ehCacheManager = new EhCacheManager(); return ehCacheManager; } @Bean(name = "securityManager") public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); securityManager.setCacheManager(ehCacheManager());//用户授权/认证信息Cache, 采用EhCache 缓存 return securityManager; } @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, String> filterChainDefinitionManager = new LinkedHashMap<>(); filterChainDefinitionManager.put("/druid/**", "anon"); filterChainDefinitionManager.put("/static/**", "anon");//静态资源不拦截 filterChainDefinitionManager.put("/login", "anon");//anon 可以理解为不拦截 filterChainDefinitionManager.put("/logout", "anon");//anon 可以理解为不拦截 filterChainDefinitionManager.put("/kaptcha", "anon");//anon 可以理解为不拦截 filterChainDefinitionManager.put("/", "anon"); filterChainDefinitionManager.put("/**", "authc,myperm");//authc未登录拦截 myperm 菜单url权限拦截 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager); shiroFilterFactoryBean.setLoginUrl("/"); //shiroFilterFactoryBean.setSuccessUrl("/"); shiroFilterFactoryBean.setUnauthorizedUrl("/"); URLPermissionsFilter urlPermissionsFilter = new URLPermissionsFilter(); Map<String,Filter> filtermap = new HashMap<>(); filtermap.put("myperm",urlPermissionsFilter);shiroFilterFactoryBean.setFilters(filtermap); return shiroFilterFactoryBean; } @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator(); daap.setProxyTargetClass(true); return daap; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(securityManager); return aasa; } //thymeleaf模板引擎和shiro整合时使用 /*@Bean(name = "shiroDialect") public ShiroDialect shiroDialect(){ return new ShiroDialect(); }*/ } ~~~ #### 2.3.4 用户菜单url访问权限拦截 > 问题:当用户登录系统后,如果当前用户角色没有某些菜单访问权限,但是该用户知道菜单的url路径,其通过浏览器直接输入路径是可以访问的! ~~~ 解决方法: 在我们配置shiro生命周期配置项时,我们做了两步操作 1:通过AexitShrioRealm将用户角色对应的菜单权限信息交给Shrio管理 2:配置拦截器 ~~~ 其实:还可以利用shiro标签和注解 ![](https://box.kancloud.cn/17b5a00511ea5464f50857b2770ec291_984x776.png) ![](https://box.kancloud.cn/503ca4706d3fecb1bd698a6a32f67a31_1037x847.png) > 然后我们通过URLPermissionsFilter 类来判断该请求是否是菜单请求,如果是菜单请求我们调用shiro为我们提供的isAccessAllowed(), > 该方法会通过我们之前交给Shrio的用户角色对应的菜单权限信息来判断此次请求是否是该用户角色范围之内的请求,如果是可以请求,不是不可以访问。 **通过注解** ~~~ @RequestMapping("/listall") @ResponseBody @RequiresPermissions("userInfo:view") public List<Userinfo> list(){ List<Userinfo> members = userInfoService.getMembers(); return members; } ~~~ ~~~ /** * 自定义shiro的URL拦截器 * 拦截目标为菜单url,根据AexitShrioRealm配置的权限决定用户有没有权限访问菜单 * 注意:这里不做数据层面的请求限制 */ public class URLPermissionsFilter extends PermissionsAuthorizationFilter { /** * @param mappedValue 指的是在声明url时指定的权限字符串, * 如/User/create.do=perms[User:create].我们要动态产生这个权限字符串,所以这个配置对我们没用 */ @SuppressWarnings("unchecked") public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { HttpServletRequest req = (HttpServletRequest) request; String path = req.getServletPath(); //用户在登陆时把系统所有的菜单项目保存到list里面 List<SysMenu> sysMenus = (List<SysMenu>)((HttpServletRequest) request).getSession().getAttribute(AuthConstants.MENUS); if(sysMenus != null){ for(SysMenu menu : sysMenus){ if(path.equals(menu.getUrl())){ //根据AexitShrioRealm配置的权限决定用户有没有权限访问菜单 return super.isAccessAllowed(request, response, buildPermissions(request)); } } } //不是菜单的,不做数据层面的请求限制 return true; } /** * 根据请求URL产生权限字符串(如:/menu),这里只产生,而比对的事交给Realm */ protected String[] buildPermissions(ServletRequest request) { String[] perms = new String[1]; HttpServletRequest req = (HttpServletRequest) request; String path = req.getServletPath(); perms[0] = path;//path直接作为权限字符串 return perms; } } ~~~ ### 2.4 SpringBoot集成kaptcha验证码 1. 添加maven依赖 ~~~ <dependency> <groupId>com.github.penggle</groupId> <artifactId>kaptcha</artifactId> <version>2.3.2</version> <exclusions> <exclusion> <artifactId>javax.servlet-api</artifactId> <groupId>javax.servlet</groupId> </exclusion> </exclusions> </dependency> ~~~ 2. 验证码配置文件 因为kaptcha的配置不符合yaml的规范,所以只能采用properties,新建kaptcha.properties ~~~ kaptcha.border=yes kaptcha.border.color=105,179,90 kaptcha.textproducer.font.color=blue kaptcha.image.width=100 kaptcha.image.height=35 kaptcha.session.key=code kaptcha.textproducer.char.length=4 kaptcha.textproducer.font.names=宋体,楷体,微软雅黑 ~~~ 3. 新建配置文件CaptchaConfig 读取配置 ~~~ @Component @PropertySource(value = {"classpath:kaptcha.properties"}) public class CaptchaConfig { @Value("${kaptcha.border}") private String border; @Value("${kaptcha.border.color}") private String borderColor; @Value("${kaptcha.textproducer.font.color}") private String fontColor; @Value("${kaptcha.image.width}") private String imageWidth; @Value("${kaptcha.image.height}") private String imageHeight; @Value("${kaptcha.session.key}") private String sessionKey; @Value("${kaptcha.textproducer.char.length}") private String charLength; @Value("${kaptcha.textproducer.font.names}") private String fontNames; @Bean(name = "captchaProducer") public DefaultKaptcha getKaptchaBean() { DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); Properties properties = new Properties(); properties.setProperty("kaptcha.border", border); properties.setProperty("kaptcha.border.color", borderColor); properties.setProperty("kaptcha.textproducer.font.color", fontColor); properties.setProperty("kaptcha.image.width", imageWidth); properties.setProperty("kaptcha.image.height", imageHeight); properties.setProperty("kaptcha.session.key", sessionKey); properties.setProperty("kaptcha.textproducer.char.length", charLength); properties.setProperty("kaptcha.textproducer.font.names", fontNames); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; } } ~~~ 4. 生成验证码 ~~~ @Resource DefaultKaptcha captchaProducer; /** * 获取验证码 */ @RequestMapping(value = "/kaptcha", method = RequestMethod.GET) public void kaptcha(HttpSession session,HttpServletResponse response) throws Exception { response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); response.addHeader("Cache-Control", "post-check=0, pre-check=0"); response.setHeader("Pragma", "no-cache"); response.setContentType("image/jpeg"); String capText = captchaProducer.createText(); //TODO 如果是前后台分离或分布式部署,这里需要使用SpringSession放到redis里面 //将验证码存到session session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText); // create the image with the text BufferedImage bi = captchaProducer.createImage(capText); ServletOutputStream out = response.getOutputStream(); // write the data out ImageIO.write(bi, "jpg", out); try { out.flush(); } finally { out.close(); } } ~~~ 5. 模拟访问界面 ~~~ <img src="${ctxPath}/kaptcha" id="kaptcha" width="100%" height="100%"/> <script> $(function(){ $("#kaptcha").on('click',function(){ $("#kaptcha").attr('src', '${ctxPath}/kaptcha?' + Math.floor(Math.random()*100) ).fadeIn(); }); }); </script> ~~~ 注意:页面需要引用js,css,需要配置静态文件目录 ~~~ spring: mvc: static-path-pattern: /static/** ~~~ ### 2.5 整合mybatis-plus分页 1. 引入pom依赖 ~~~ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <shiro.version>1.4.0</shiro.version> <mybatisplus-spring-boot-starter.version>1.0.4</mybatisplus-spring-boot-starter.version> <mybatisplus.version>2.1-gamma</mybatisplus.version> </properties <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatisplus-spring-boot-starter</artifactId> <version>${mybatisplus-spring-boot-starter.version}</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>${mybatisplus.version}</version> </dependency> ~~~ 2. 配置分页 ~~~ package com.aixin.tuna.weblearn.weblearn.config; import com.baomidou.mybatisplus.plugins.PaginationInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @MapperScan(basePackages = {"com.aixin.tuna.**.mapper"}) public class MybatisPlusConfig { /** * mybatis-plus分页插件<br> * 文档:http://mp.baomidou.com<br> */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); } } ~~~ 3. controller ~~~ @GetMapping(value = "/list") @ResponseBody public List<FederatedCompany> list(@RequestParam(required = false) Integer pageIndex, @RequestParam(required = false) Integer pageSize) { Page<FederatedCompany> federatedCompanyPage; if (pageIndex !=null && pageSize !=null) { federatedCompanyPage = new Page<>(pageIndex,pageSize); //创建page对象,传入当前页和每页显示条目数 } else { federatedCompanyPage = new Page<>(1,Integer.MAX_VALUE); } return federatedCompanyService.getCompanyList(federatedCompanyPage); } ~~~ 4. service ~~~ @Resource FederatedCompanyMapper federatedCompanyMapper; public List<FederatedCompany> getCompanyList(Page<FederatedCompany> page) { return federatedCompanyMapper.list(page); } ~~~ 5. mapper接口 接口接收page对象,自动分页 ~~~ List<FederatedCompany> list(Page<FederatedCompany> page); ~~~ 6. mapper.xml ~~~ <select id="list" resultType="com.aixin.tuna.weblearn.weblearn.enterprise.model.FederatedCompany"> select * from federated_company </select> ~~~ ![](https://box.kancloud.cn/6a07adca5b67f260a45da054f25822da_863x389.png)