Java开发
SpringBoot框架之@Controller和@RestController的区别?

1.@RestController注解相当于@ResponseBody+@Controller合在一起的作用。2.如果只是使用@RestController注解Controller,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是Return里的内容。3.如果需要返回到指定页面,则需要用@Controller配合视图解析器InternalResourceViewResolver才行。4.如果需要返回JSON,XML或自定义mediaType内容到页面,则需要在对应的方法上加上@ResponseBody注解。)

Tags: JAVA
Java开发
MyBatis之where关键字与<where>标签的区别

1.在使用mybatis的动态sql时,有时候遇到根据条件判断添加where后面的筛选条件的情况,会出现多余的AND或者OR:2.使用where关键字:2.1当第一个参数为空时,拼接后的sql为:select*fromtdwhereandphone=.......;2.2当所有的参数都为空时,拼接后的sql为:select*fromtdwhere.....,显然这样的sql不是完整的sql,执行时会报错.3.使用where标签时:3.1当第一个参数为空时,拼接后的sql为:select*fromtdwherephone=......(若语句的开头为AND或者OR时,where元素会将他们去除).3.2当所有的参数都为空时,拼接后的sql为:select*fromtd.(where元素只会在至少有一个子元素的条件返回SQL子句的情况下才去插入“WHERE”子句)。

Tags: JAVA
工作日志
【基于ELK搭建日志分析系统】- ElasticSearch安装

存储集群应与业务服务器不在同一台机器,此处搭建ES存储集群使用单节点的方式。ES集群机器需要实现安装配置好JAVA_HOME环境变量。安装配置下载,版本应与filebeat版本一致:官网地址解压修改elasticsearch.yml配置文件:其中cluster.name避免使用默认名称,各节点中配置为相同的名称,ES就会自动搜索加入启动elasticsearch:==ES集群启动不允许使用root账户==,所以应该先创建一个用户,使用新创建的用户启动elasticsearchgroupaddelkuseradd-gelkelkpasswdelk#创建elk用户密码chown-Relk:elk$ELASTICSEARCH_HOMEsuelk$ELASTICSEARCH_HOME/bin/elasticsearch-d#后台启动ES启动完成后测试,浏览器输入http://host:9200测试,有如下结果则为正常:问题解决启动elasticsearch时出现:elasticsearch:which:nojavain(/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin)[解决]在$ES_HOME/config/elasticsearch文件中加入以下配置:JAVA_HOME=/usr/local/jdk1.8maxnumberofthreads[1024]foruser[elk]likelytoolow,increasetoatleast[2048].[解决]切换到root用户,进入limits.d目录下修改配置文件:vi/etc/security/limits.d/90-nproc.conf找到并修改为softnproc2048hardnproc4096maxfiledescriptors[4096]forelasticsearchprocesslikelytoolow,increasetoatleast[65536][解决]切换到root用户,进入limits.d目录下修改配置文件。vi/etc/security/limits.d/90-nproc.conf找到并修改为softnofile65536hardnofile131072maxvirtualmemoryareasvm.max_map_count[65530]likelytoolow,increasetoatleast[262144][解决]修改:vim/etc/sysctl.conf添加下面配置:vm.max_map_count=655360并执行命令:sysctl-p

个人随笔
Centos7安装Redis

一、安装Redis1.下载Rediswgethttp://download.redis.io/releases/redis-4.0.6.tar.gz2.解压tar-zxvfredis-4.0.6.tar.gz3.yum安装gccyuminstall-ygcc4.编译安装rediscdredis-4.0.6makeMALLOC=libccdsrc&amp;&amp;makeinstall二、启动Redis1.在/etc目录下新建redis目录mkdir-p/etc/redis2.将/usr/local/redis-4.0.6/redis.conf文件复制一份到/etc/redis目录下,并命名为6379.confcp/usr/local/redis-4.0.6/redis.conf/etc/redis/6379.conf3.将redis的启动脚本复制一份放到/etc/init.d目录下cp/usr/local/redis-4.0.6/utils/redis_init_script/etc/init.d/redisd4.设置redis开机自启动cd/etc/init.dchkconfigredisdonserviceredisddoesnotsupportchkconfig看结果是redisd不支持chkconfig,编辑redisd文件,加入如下注释:#chkconfig:23459010#description:Redisisapersistentkey-valuedatabasechkconfigredisdon启动redis:serviceredisdstart停止redis:serviceredisdstop

Java开发
基于SpringSecurity的用户权限控制

1.说明本文主要介绍基于SpringSecurity的用户权限控制的简单实现。1.1环境版本SpringBoot:2.0.7SpringSecurity:5.0.10JDK:1.82.项目配置2.1引入maven包&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;projectxmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;&lt;parent&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;&lt;version&gt;2.0.7.RELEASE&lt;/version&gt;&lt;relativePath/&gt;&lt;!--lookupparentfromrepository--&gt;&lt;/parent&gt;&lt;groupId&gt;cn.coralcloud&lt;/groupId&gt;&lt;artifactId&gt;security&lt;/artifactId&gt;&lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;&lt;name&gt;security&lt;/name&gt;&lt;description&gt;DemoSecurityprojectforSpringBoot&lt;/description&gt;&lt;properties&gt;&lt;java.version&gt;1.8&lt;/java.version&gt;&lt;/properties&gt;&lt;dependencies&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-data-redis&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-jdbc&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.mybatis.spring.boot&lt;/groupId&gt;&lt;artifactId&gt;mybatis-spring-boot-starter&lt;/artifactId&gt;&lt;version&gt;2.1.1&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.session&lt;/groupId&gt;&lt;artifactId&gt;spring-session-data-redis&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.session&lt;/groupId&gt;&lt;artifactId&gt;spring-session-jdbc&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-devtools&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;mysql&lt;/groupId&gt;&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-configuration-processor&lt;/artifactId&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;optional&gt;true&lt;/optional&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;&lt;artifactId&gt;jackson-annotations&lt;/artifactId&gt;&lt;version&gt;2.9.9&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;com.google.code.gson&lt;/groupId&gt;&lt;artifactId&gt;gson&lt;/artifactId&gt;&lt;version&gt;2.8.5&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;com.alibaba&lt;/groupId&gt;&lt;artifactId&gt;fastjson&lt;/artifactId&gt;&lt;version&gt;1.2.60&lt;/version&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;com.google.guava&lt;/groupId&gt;&lt;artifactId&gt;guava&lt;/artifactId&gt;&lt;version&gt;22.0&lt;/version&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;build&gt;&lt;plugins&gt;&lt;plugin&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;/project&gt;2.2application.ymlserver.port:9091spring.application.name:spring-webspring.http.encoding.charset:utf8spring:session:store-type:redisredis:host:localhostport:6379password:123456datasource:driver-class-name:com.mysql.jdbc.Driverurl:jdbc:mysql://127.0.0.1:3306/security?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false&amp;serverTimezone=Asia/Shanghaiusername:rootpassword:123456servlet:multipart:max-file-size:1024MBmax-request-size:1024MBmybatis:mapper-locations:classpath*:mapper/*Mapper.xmltype-aliases-package:cn.coralcloud.security.model2.3数据库初始化脚本/*NavicatMySQLDataTransferSourceServer:localhostSourceServerVersion:50644SourceHost:localhost:3306SourceDatabase:securityTargetServerType:MYSQLTargetServerVersion:50644FileEncoding:65001Date:2019-12-0216:28:44*/SETFOREIGN_KEY_CHECKS=0;--------------------------------Tablestructureforrole------------------------------DROPTABLEIFEXISTS`role`;CREATETABLE`role`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`name`varchar(50)NOTNULL,`nickname`varchar(50)NOTNULL,`system`bit(1)NOTNULLDEFAULTb&#39;0&#39;,`description`varchar(500)DEFAULTNULL,`permission`mediumtext,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=2DEFAULTCHARSET=utf8mb4;--------------------------------Tablestructureforuser------------------------------DROPTABLEIFEXISTS`user`;CREATETABLE`user`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`username`varchar(50)NOTNULL,`password`varchar(300)NOTNULL,`role`varchar(500)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8mb4;3.系统初始化文件3.1users.json[{&quot;username&quot;:&quot;admin&quot;,&quot;password&quot;:&quot;ea48576f30be1669971699c09ad05c94&quot;,&quot;role&quot;:&quot;ROLE_ADMINISTRATOR&quot;}]3.2roles.json[{&quot;name&quot;:&quot;ROLE_ADMINISTRATOR&quot;,&quot;nickname&quot;:&quot;管理员&quot;,&quot;description&quot;:&quot;系统超级管理员,不允许用户更改&quot;,&quot;system&quot;:true,&quot;permissions&quot;:[{&quot;resourceId&quot;:&quot;user&quot;,&quot;resourceName&quot;:&quot;用户管理&quot;,&quot;privileges&quot;:{&quot;list&quot;:&quot;查看用户列表&quot;,&quot;add&quot;:&quot;新增用户&quot;,&quot;update&quot;:&quot;修改用户信息&quot;,&quot;delete&quot;:&quot;删除用户&quot;}},{&quot;resourceId&quot;:&quot;permission&quot;,&quot;resourceName&quot;:&quot;权限&quot;,&quot;privileges&quot;:{&quot;read&quot;:&quot;查看权限&quot;,&quot;write&quot;:&quot;新增权限&quot;,&quot;update&quot;:&quot;更新权限&quot;,&quot;delete&quot;:&quot;删除权限&quot;}}]}]4.数据持久化DAO层4.1UserDao.javapackagecn.coralcloud.security.dao;importcn.coralcloud.security.model.User;importorg.apache.ibatis.annotations.Mapper;importorg.springframework.stereotype.Component;importjava.util.List;/***@authorgeff*@nameUserDao*@description*@date2019-11-2910:31*/@Component@MapperpublicinterfaceUserDao{/***根据用户名查找*@date2019/11/2915:24*@authorgeff*@paramusernameusername*@returncn.coralcloud.security.model.User*/UserfindByUsername(Stringusername);/***创建用户*@date2019/11/2915:24*@authorgeff*@paramuseruser*/voidsave(Useruser);/***获取用户列表*@date2019/11/2915:24*@authorgeff*@param*@returnjava.util.List&lt;cn.coralcloud.security.model.User&gt;*/List&lt;User&gt;list();}4.2RoleDao.javapackagecn.coralcloud.security.dao;importcn.coralcloud.security.model.Role;importorg.apache.ibatis.annotations.Mapper;importorg.springframework.stereotype.Component;/***@authorgeff*@nameRoleDao*@description*@date2019-11-2910:31*/@Component@MapperpublicinterfaceRoleDao{/***根据名称查找*@date2019/11/2915:23*@authorgeff*@paramnamename*@returncn.coralcloud.security.model.Role*/RolefindByName(Stringname);/***保存数据*@date2019/11/2915:23*@authorgeff*@paramrolerole*/voidsave(Rolerole);}4.3UserMapper.xml&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;!DOCTYPEmapperPUBLIC&quot;-//mybatis.org//DTDMapper3.0//EN&quot;&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;&lt;mappernamespace=&quot;cn.coralcloud.security.dao.UserDao&quot;&gt;&lt;resultMapid=&quot;userMapper&quot;type=&quot;cn.coralcloud.security.model.User&quot;&gt;&lt;idproperty=&quot;id&quot;column=&quot;id&quot;/&gt;&lt;/resultMap&gt;&lt;selectid=&quot;findByUsername&quot;parameterType=&quot;String&quot;resultMap=&quot;userMapper&quot;&gt;select*from`user`where`username`=#{username}&lt;/select&gt;&lt;selectid=&quot;list&quot;resultType=&quot;cn.coralcloud.security.model.User&quot;&gt;select*from`user`&lt;/select&gt;&lt;insertid=&quot;save&quot;parameterType=&quot;cn.coralcloud.security.model.User&quot;useGeneratedKeys=&quot;true&quot;keyProperty=&quot;id&quot;&gt;insertinto`user`(username,password,role)values(#{username},#{password},#{role})&lt;/insert&gt;&lt;/mapper&gt;4.4RoleMapper.xml&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;!DOCTYPEmapperPUBLIC&quot;-//mybatis.org//DTDMapper3.0//EN&quot;&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;&lt;mappernamespace=&quot;cn.coralcloud.security.dao.RoleDao&quot;&gt;&lt;resultMapid=&quot;roleMapper&quot;type=&quot;cn.coralcloud.security.model.Role&quot;&gt;&lt;idproperty=&quot;id&quot;column=&quot;id&quot;/&gt;&lt;/resultMap&gt;&lt;selectid=&quot;findByName&quot;parameterType=&quot;String&quot;resultMap=&quot;roleMapper&quot;&gt;select*fromrolewhere`name`=#{name}&lt;/select&gt;&lt;insertid=&quot;save&quot;parameterType=&quot;cn.coralcloud.security.model.Role&quot;useGeneratedKeys=&quot;true&quot;keyProperty=&quot;id&quot;&gt;insertintorole(name,nickname,description,system,permission)values(#{name},#{nickname},#{description},#{system},#{permission})&lt;/insert&gt;&lt;/mapper&gt;5.处理用户权限认证逻辑5.1SpringSecurity配置要在项目中使用@PreAuthorize等注解实现方法级别权限控制,则需要在项目启动类上添加注解@EnableGlobalMethodSecurity(prePostEnabled=true),本文项目启动类:packagecn.coralcloud.security;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.boot.builder.SpringApplicationBuilder;importorg.springframework.boot.web.servlet.support.SpringBootServletInitializer;importorg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;/***@authorgeff*/@SpringBootApplication@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassSecurityApplicationextendsSpringBootServletInitializer{@OverrideprotectedSpringApplicationBuilderconfigure(SpringApplicationBuilderapplicationBuilder){returnapplicationBuilder.sources(SecurityApplication.class);}publicstaticvoidmain(String[]args){SpringApplication.run(SecurityApplication.class,args);}}5.2用户对象SpringSecurity自带的有UserDetails接口主要保存用户对象数据,所以我们的用户对象需要实现UserDetails接口packagecn.coralcloud.security.model;importcom.fasterxml.jackson.annotation.JsonIgnore;importlombok.Data;importlombok.NoArgsConstructor;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importjava.io.Serializable;importjava.util.*;/***@authorgeff*/@Data@NoArgsConstructorpublicclassUserimplementsUserDetails,Serializable{privateLongid;/***用户登录名*/privateStringusername;/***用户登录密码,用户的密码不应该暴露给客户端*/@JsonIgnoreprivateStringpassword;/***用户在系统中的角色列表,将根据角色对用户操作权限进行限制*/privateStringrole;privateList&lt;Role&gt;roles;@OverridepublicCollection&lt;?extendsGrantedAuthority&gt;getAuthorities(){Collection&lt;GrantedAuthority&gt;authorities=newArrayList&lt;&gt;();if(roles!=null){for(Rolerole:roles){if(role==null){continue;}for(Permissionpermission:role.getPermissions()){for(Stringprivilege:permission.getPrivileges().keySet()){authorities.add(newSimpleGrantedAuthority(String.format(&quot;%s-%s&quot;,permission.getResourceId(),privilege)));}}}}returnauthorities;}@OverridepublicbooleanisAccountNonExpired(){returntrue;}@OverridepublicbooleanisAccountNonLocked(){returntrue;}@OverridepublicbooleanisCredentialsNonExpired(){returntrue;}@OverridepublicbooleanisEnabled(){returntrue;}}在用户对象的publicCollection&lt;?extendsGrantedAuthority&gt;getAuthorities()方法中,需要根据用户当前角色生成当前用户权限列表,本文权限机制使用resourceId-privilege方式5.3角色对象Role.javapackagecn.coralcloud.security.model;importcom.alibaba.fastjson.JSON;importlombok.Data;importorg.springframework.util.StringUtils;importjava.io.Serializable;importjava.util.List;/***@authorgeff*@nameRole*@description*@date2019-11-2910:08*/@DatapublicclassRoleimplementsSerializable{privateLongid;/***角色名,用于权限校验*/privateStringname;/***角色中文名,用于显示*/privateStringnickname;/***角色描述信息*/privateStringdescription;/***是否为内置*/privateBooleansystem;/***角色可进行的操作列表*/privateList&lt;Permission&gt;permissions;privateStringpermission;/***SpringSecurity4.0以上版本角色都默认以&#39;ROLE_&#39;开头*@paramnamename*/publicvoidsetName(Stringname){if(!name.contains(&quot;ROLE_&quot;)){this.name=&quot;ROLE_&quot;+name;}else{this.name=name;}}publicList&lt;Permission&gt;getPermissions(){if(permissions==null){if(!StringUtils.isEmpty(permission)){this.permissions=JSON.parseArray(permission,Permission.class);}}returnpermissions;}publicStringgetPermission(){if(StringUtils.isEmpty(permission)){this.permission=JSON.toJSONString(permissions);}returnpermission;}}SpringSecurity4.0以上所有的角色名称默认都要以ROLE_开头,所有本文在获取角色名称是会自动添加前缀。为了简单方便,本文角色对象的permission字段保存着该角色的所有权限列表的JSON字符串5.4权限对象Permission.javapackagecn.coralcloud.security.model;importlombok.Data;importjava.io.Serializable;importjava.util.Map;/***@authorgeff*@namePermission*@description*@date2019-11-2910:07*/@DatapublicclassPermissionimplementsSerializable{privateStringresourceId;privateStringresourceName;privateMap&lt;String,String&gt;privileges;privatebooleanabandon=false;}5.5统一数据返回对象因为是前后端分离项目,所以本文封装了统一数据返回对象Response类packagecn.coralcloud.security.model;importjava.io.Serializable;/***响应*@authorgeff*/publicclassResponse&lt;T&gt;implementsSerializable{privatefinalstaticintSUCCESS=0;privatefinalstaticintERROR=-1;privateintcode;privateTdata;privateStringmessage;publicintgetCode(){returncode;}publicvoidsetCode(intcode){this.code=code;}publicStringgetMessage(){returnmessage;}publicvoidsetMessage(Stringmessage){this.message=message;}publicTgetData(){returndata;}publicvoidsetData(Tdata){this.data=data;}publicstatic&lt;T&gt;Response&lt;T&gt;ok(){Response&lt;T&gt;response=newResponse&lt;&gt;();response.setCode(SUCCESS);returnresponse;}publicstatic&lt;T&gt;Response&lt;T&gt;ok(Tdata){Response&lt;T&gt;response=newResponse&lt;&gt;();response.setCode(SUCCESS);response.setData(data);returnresponse;}publicstatic&lt;T&gt;Response&lt;T&gt;fail(Stringmessage){Response&lt;T&gt;response=newResponse&lt;&gt;();response.setCode(ERROR);response.setMessage(message);returnresponse;}publicstatic&lt;T&gt;Response&lt;T&gt;fail(intcode,Stringmessage){Response&lt;T&gt;response=newResponse&lt;&gt;();response.setCode(code);response.setMessage(message);returnresponse;}}5.6UserDetailsService接口获取用户信息SpringSecurity同时在用户登录认证时会通过调用UserDetailsService的loadUserByUsername来获取当前登录的用户信息,当用户认证通过后会将用户对象保存到自定义的Token对象中。所以本文需要实现UserDetailsService接口,完成loadUserByUsername方法:packagecn.coralcloud.security.service;importcn.coralcloud.security.dao.RoleDao;importcn.coralcloud.security.model.Role;importcn.coralcloud.security.model.User;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;importorg.springframework.util.StringUtils;importjava.util.ArrayList;importjava.util.List;/***@authorgeff*/@Service(&quot;myUserDetailsService&quot;)publicclassUserDetailsServiceImplimplementsUserDetailsService{@AutowiredprivateUserServiceuserService;@AutowiredprivateRoleDaoroleDao;@OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{Useruser=userService.findByUsername(username);if(user==null){thrownewUsernameNotFoundException(String.format(&quot;Nouserfoundwithusername:%s&quot;,username));}if(!StringUtils.isEmpty(user.getRole())){String[]roles=user.getRole().split(&quot;,&quot;);List&lt;Role&gt;roleList=newArrayList&lt;&gt;();for(StringroleName:roles){Rolerole=roleDao.findByName(roleName);roleList.add(role);}user.setRoles(roleList);}returnuser;}}5.7自定义方法权限处理器自定义方法权限处理器需要实现PermissionEvaluator接口,完成publicbooleanhasPermission(Authenticationauthentication,ObjecttargetDomainObject,Objectpermission)方法。packagecn.coralcloud.security.config;importcn.coralcloud.security.model.User;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.access.PermissionEvaluator;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.GrantedAuthority;importjava.io.Serializable;/***@authorgeff*/@ConfigurationpublicclassMyPermissionEvaluatorimplementsPermissionEvaluator{@OverridepublicbooleanhasPermission(Authenticationauthentication,ObjecttargetDomainObject,Objectpermission){booleanaccessable=false;if(authentication.getPrincipal()instanceofUser){Stringprivilege=targetDomainObject+&quot;-&quot;+permission;for(GrantedAuthorityauthority:authentication.getAuthorities()){if(privilege.equalsIgnoreCase(authority.getAuthority())){accessable=true;break;}}returnaccessable;}returnfalse;}@OverridepublicbooleanhasPermission(Authenticationauthentication,SerializabletargetId,StringtargetType,Objectpermission){//TODOAuto-generatedmethodstubreturnfalse;}}本文通过根据resourceId-privilege方式验证权限5.8系统初始化配置类本文系统初始化配置类主要实现在系统启动时根据配置的users.json和roles.json自动生成初始化用户和角色信息。packagecn.coralcloud.security.component;importcn.coralcloud.security.dao.RoleDao;importcn.coralcloud.security.dao.UserDao;importcn.coralcloud.security.model.Role;importcn.coralcloud.security.model.User;importcom.google.gson.GsonBuilder;importcom.google.gson.reflect.TypeToken;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importorg.springframework.beans.factory.annotation.Value;importjavax.annotation.PostConstruct;importjava.io.InputStream;importjava.io.InputStreamReader;importjava.lang.reflect.Type;importjava.nio.charset.StandardCharsets;importjava.util.ArrayList;/***系统初始化配置类,主要用于加载内置数据到目标数据库上*@authorgeff*/@ComponentpublicclassSystemInitializer{@Value(&quot;${initialzation.file.users:users.json}&quot;)privateStringuserFileName;@Value(&quot;${initialzation.file.roles:roles.json}&quot;)privateStringroleFileName;@AutowiredprivateUserDaouserDao;@AutowiredprivateRoleDaoroleDao;@AutowiredprivateGsonBuildergsonBuilder;@PostConstructpublicbooleaninitialize(){try{InputStreamuserInputStream=getClass().getClassLoader().getResourceAsStream(userFileName);if(userInputStream==null){thrownewException(&quot;initialzationuserfilenotfound:&quot;+userFileName);}InputStreamroleInputStream=getClass().getClassLoader().getResourceAsStream(roleFileName);if(roleInputStream==null){thrownewException(&quot;initialzationrolefilenotfound:&quot;+roleFileName);}//导入初始的系统超级管理员角色TyperoleTokenType=newTypeToken&lt;ArrayList&lt;Role&gt;&gt;(){}.getType();ArrayList&lt;Role&gt;roles=gsonBuilder.create().fromJson(newInputStreamReader(roleInputStream,StandardCharsets.UTF_8),roleTokenType);for(Rolerole:roles){if(roleDao.findByName(role.getName())==null){roleDao.save(role);}}//导入初始的系统管理员用户TypeteacherTokenType=newTypeToken&lt;ArrayList&lt;User&gt;&gt;(){}.getType();ArrayList&lt;User&gt;users=gsonBuilder.create().fromJson(newInputStreamReader(userInputStream,StandardCharsets.UTF_8),teacherTokenType);for(Useruser:users){if(userDao.findByUsername(user.getUsername())==null){userDao.save(user);}}}catch(Exceptione){e.printStackTrace();}returntrue;}}6.用户认证相关自定义实现6.1.自定义用户认证过滤器​用户认证过滤器拦截用户发送的认证请求,然后从请求中获取用户账号和密码等认证信息并封装成一个未认证的AthenticationToken对象,然后调用AuthenticationManager对AthenticationToken进行认证。​自定义用户认证过滤器需要继承AbstractAuthenticationProcessingFilter,然后重写attemptAuthentication方法,在方法内部根据请求参数封装成未认证的AthenticationToken对象packagecn.coralcloud.security.component;importorg.springframework.security.authentication.AuthenticationServiceException;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;importorg.springframework.security.web.util.matcher.AntPathRequestMatcher;importorg.springframework.util.StringUtils;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;/***自定义的用户名密码认证过滤器*@authorgeff*/publicclassAuthFilterextendsAbstractAuthenticationProcessingFilter{privatestaticStringhttpMethod=&quot;POST&quot;;publicAuthFilter(){/**设置该过滤器对POST请求/api/user/login进行拦截*/super(newAntPathRequestMatcher(&quot;/api/user/login&quot;,httpMethod));}@OverridepublicAuthenticationattemptAuthentication(HttpServletRequestrequest,HttpServletResponseresponse)throwsAuthenticationException{if(!request.getMethod().equals(httpMethod)){thrownewAuthenticationServiceException(&quot;Authenticationmethodnotsupported:&quot;+request.getMethod());}else{/**从http请求中获取用户输入的用户名和密码信息*这里接收的是form形式的参数,如果要接收json形式的参数,修改这里即可*/Stringusername=this.obtainUsername(request);Stringpassword=this.obtainPassword(request);if(StringUtils.isEmpty(username)&amp;&amp;StringUtils.isEmpty(password)){thrownewUsernameNotFoundException(&quot;用户名或密码错误&quot;);}/**使用用户输入的用户名和密码信息创建一个未认证的用户认证Token*/AuthTokenauthRequest=newAuthToken(username,password);/**设置一些详情信息*/this.setDetails(request,authRequest);/**通过AuthenticationManager调用相应的AuthenticationProvider进行用户认证*/returnthis.getAuthenticationManager().authenticate(authRequest);}}privateStringobtainUsername(HttpServletRequestrequest){StringusernameParameter=&quot;username&quot;;returnrequest.getParameter(usernameParameter);}privateStringobtainPassword(HttpServletRequestrequest){StringpasswordParameter=&quot;password&quot;;returnrequest.getParameter(passwordParameter);}privatevoidsetDetails(HttpServletRequestrequest,AuthTokenauthRequest){authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));}}自定义用户认证拦截器要在构造方法中指定拦截的认证请求(本文中是POST类型的/api/user/login请求,可根据需求设置),并在attemptAuthentication()方法中实现获取用户认证信息、封装AuthenticationToken对象、调用AuthenticationManager对AuthenticationToken进行认证等逻辑。6.2自定义用户认证处理器用户认证处理器主要是对用户提交的认证信息进行认证,SpringSecurity默认实现的认证处理器的认证处理逻辑并不一定符合所有的业务需求(例如,默认的认证处理无法处理验证码),因此,可以自定义用户认证处理器。自定义的用户认证处理器,需要实现AuthenticationProvider接口,主要是实现publicAuthenticationauthenticate(Authenticationauthentication)方法和publicbooleansupports(Class&lt;?&gt;authentication)方法,前者主要是实现具体的认证逻辑,后者主要是指定认证处理器能对哪种AuthenticationToken对象进行认证。packagecn.coralcloud.security.component;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.authentication.AuthenticationProvider;importorg.springframework.security.authentication.BadCredentialsException;importorg.springframework.security.authentication.dao.DaoAuthenticationProvider;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.stereotype.Component;/***@authorgeff*@nameAuthProvider*@description*登录认证的Provider,自定义实现了{@linkAuthenticationProvider}&lt;br&gt;*Provider默认实现是{@linkDaoAuthenticationProvider}&lt;br&gt;*{@linkUsernamePasswordAuthenticationFilter}调用=&gt;{@linkAuthenticationManager}=&gt;{@linkAuthenticationProvider}验证&lt;br&gt;**@date2019-11-2915:52*/@Slf4j@ComponentpublicclassAuthProviderimplementsAuthenticationProvider{privatefinalUserDetailsServiceuserDetailService;privatefinalPasswordEncoderpasswordEncoder;@AutowiredpublicAuthProvider(@Qualifier(&quot;myUserDetailsService&quot;)UserDetailsServiceuserDetailService,PasswordEncoderpasswordEncoder){this.userDetailService=userDetailService;this.passwordEncoder=passwordEncoder;}@OverridepublicAuthenticationauthenticate(Authenticationauthentication)throwsAuthenticationException{/**将未认证的Authentication转换成自定义的用户认证Token*/AuthTokenauthenticationToken=(AuthToken)authentication;/**根据用户Token中的用户名查找用户信息,如果有该用户信息,则验证用户密码是否正确*/UserDetailsuser=userDetailService.loadUserByUsername((String)(authenticationToken.getPrincipal()));if(user==null){thrownewBadCredentialsException(&quot;用户名或密码不正确&quot;);}elseif(!this.passwordEncoder.matches((CharSequence)authenticationToken.getCredentials(),user.getPassword())){thrownewBadCredentialsException(&quot;用户名或密码不正确&quot;);}/**认证成功则创建一个已认证的用户认证Token*/AuthTokenauthenticationResult=newAuthToken(user,user.getPassword(),user.getAuthorities());/**设置一些详情信息*/authenticationResult.setDetails(authenticationToken.getDetails());returnauthenticationResult;}/***是否支持处理当前Authentication对象类似*/@Overridepublicbooleansupports(Class&lt;?&gt;authentication){returntrue;}}6.3自定义用户认证对象用户认证对象是在用户认证拦截器中创建的,在用户认证处理器中使用的。用户认证对象(AuthenticationToken)中封装的是用户认证信息,例如UsernamePasswordAuthenticationToken中封装的是用户名和密码。实际业务中,可能需要根据不同的用户信息进行认证(例如,手机号和验证码),此时就需要自定义用户认证对象。自定义的用户认证对象,需要继承AbstractAuthenticationToken类,并设定根据认证时使用的是哪些信息。packagecn.coralcloud.security.component;importorg.springframework.security.authentication.AbstractAuthenticationToken;importorg.springframework.security.core.GrantedAuthority;importjava.util.Collection;/***自定义的用户名密码认证对象*@authorgeff*/publicclassAuthTokenextendsAbstractAuthenticationToken{/***用户名*/privatefinalObjectprincipal;/***密码*/privateObjectcredentials;/***创建未认证的用户名密码认证对象*/publicAuthToken(Objectprincipal,Objectcredentials){super(null);this.principal=principal;this.credentials=credentials;this.setAuthenticated(false);}/***创建已认证的用户密码认证对象*/publicAuthToken(Objectprincipal,Objectcredentials,Collection&lt;?extendsGrantedAuthority&gt;authorities){super(authorities);this.principal=principal;this.credentials=credentials;super.setAuthenticated(true);}@OverridepublicObjectgetCredentials(){returnthis.credentials;}@OverridepublicObjectgetPrincipal(){returnthis.principal;}@OverridepublicvoidsetAuthenticated(booleanisAuthenticated)throwsIllegalArgumentException{if(isAuthenticated){thrownewIllegalArgumentException(&quot;Cannotsetthistokentotrusted-useconstructorwhichtakesaGrantedAuthoritylistinstead&quot;);}else{super.setAuthenticated(false);}}@OverridepublicvoideraseCredentials(){super.eraseCredentials();this.credentials=null;}}6.4自定义用户认证成功处理器用户认证成功处理器在用户认证成功之后调用,主要是执行一些额外的操作(例如,操作Cookie、页面跳转等)。自定义的用户认证成功处理器可以通过实现AuthenticationSuccessHandler接口,或者通过继承AbstractAuthenticationTargetUrlRequestHandler类及其子类来实现。本文自定义的用户认证成功处理器是通过继承AbstractAuthenticationTargetUrlRequestHandler的子类SavedRequestAwareAuthenticationSuccessHandler来实现的。packagecn.coralcloud.security.component;importcn.coralcloud.security.model.User;importcom.fasterxml.jackson.databind.ObjectMapper;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.Authentication;importorg.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importjava.io.IOException;/***自定义的用户认证成功处理器*@authorgeff*/@Component@Slf4jpublicclassAuthSuccessHandlerextendsSavedRequestAwareAuthenticationSuccessHandler{@AutowiredprivateObjectMapperobjectMapper;publicAuthSuccessHandler(){}@OverridepublicvoidonAuthenticationSuccess(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication)throwsIOException{//认证成功返回jsonUseruser=(User)authentication.getPrincipal();//写入session?HttpSessionsession=request.getSession();session.setAttribute(&quot;User&quot;,user);StringjsonStr=objectMapper.writeValueAsString(user);log.info(&quot;认证成功:{}&quot;,jsonStr);response.setContentType(&quot;application/json;charset=UTF-8&quot;);response.getWriter().write(jsonStr);}}6.5自定义用户认证失败处理器用户认证失败处理器是在用户认证失败之后调用,主要是执行一些额外的操作(例如操作Cookie、页面跳转、返回错误信息等)。自定义的用户认证失败处理器可以通过实现AuthenticationFailureHandler接口,或者通过继承AuthenticationFailureHandler接口的其它实现类来实现。本文自定义的用户认证失败处理器是通过继承AuthenticationFailureHandler接口的实现类SimpleUrlAuthenticationFailureHandler来实现的。packagecn.coralcloud.security.component;importcn.coralcloud.security.model.Response;importcom.fasterxml.jackson.databind.ObjectMapper;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.HttpStatus;importorg.springframework.security.core.AuthenticationException;importorg.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/***自定义的用户认证失败处理器*@authorgeff*/@Component@Slf4jpublicclassAuthFailureHandlerextendsSimpleUrlAuthenticationFailureHandler{@AutowiredprivateObjectMapperobjectMapper;@OverridepublicvoidonAuthenticationFailure(HttpServletRequestrequest,HttpServletResponseresponse,AuthenticationExceptionexception)throwsIOException{/**返回JSON*/log.error(&quot;认证失败:{}&quot;,exception.getMessage());Responseres=Response.fail(-1,exception.getMessage());response.setStatus(HttpStatus.OK.value());response.setContentType(&quot;application/json;charset=UTF-8&quot;);response.getWriter().write(objectMapper.writeValueAsString(res));response.getWriter().flush();}}6.6自定义访问拒绝处理器自定义访问拒绝处理器用来解决认证过的用户访问无权限资源时的异常。前后端分离的情况下可以通过自定义访问拒绝处理器实现JSON格式的数据返回,自定义访问拒绝处理器通过实现AccessDeniedHandler接口,然后实现publicvoidhandle(HttpServletRequestrequest,HttpServletResponseresponse,AccessDeniedExceptione)方法,在方法内部处理返回数据。packagecn.coralcloud.security.component;importcn.coralcloud.security.model.Response;importcom.fasterxml.jackson.databind.ObjectMapper;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.http.HttpStatus;importorg.springframework.security.access.AccessDeniedException;importorg.springframework.security.web.access.AccessDeniedHandler;importorg.springframework.stereotype.Component;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;/***@authorgeff*@nameAuthAccessDeniedHandler*@description*@date2019-11-2916:57*/@Slf4j@ComponentpublicclassAuthAccessDeniedHandlerimplementsAccessDeniedHandler{@AutowiredprivateObjectMapperobjectMapper;@Overridepublicvoidhandle(HttpServletRequestrequest,HttpServletResponseresponse,AccessDeniedExceptione)throwsIOException{log.error(&quot;无权访问:{}&quot;,e.getMessage());Responseres=Response.fail(401,&quot;无权访问&quot;);response.setStatus(HttpStatus.UNAUTHORIZED.value());response.setContentType(&quot;application/json;charset=UTF-8&quot;);response.getWriter().write(objectMapper.writeValueAsString(res));response.getWriter().flush();}}6.7自定义加密类本文密码使用Md5(password,salt)的形式,所以需要自定义SpringSecurity加密类,然后再Config配置类注入自定义加密类需要实现PasswordEncoder,完成encode和matches方法packagecn.coralcloud.security.component;importcn.coralcloud.security.utils.SecretUtils;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.util.StringUtils;importjava.util.Objects;/***@authorgeff*@nameMd5SaltPasswordEncoder*@description*@date2019-12-0209:27*/publicclassMd5SaltPasswordEncoderimplementsPasswordEncoder{@OverridepublicStringencode(CharSequencecharSequence){Stringstring=charSequence.toString();String[]array=string.split(&quot;,&quot;);Stringsalt=&quot;&quot;;if(array.length&gt;1){salt=array[1];}returnSecretUtils.md5(array[0],salt);}@Overridepublicbooleanmatches(CharSequencecharSequence,Strings){if(!StringUtils.isEmpty(s)){StringencodePassword=encode(charSequence);returnObjects.equals(encodePassword,s);}returnfalse;}}7.SpringSecurity相关配置类7.1自定义的用户名密码认证配置类packagecn.coralcloud.security.component;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.authentication.AuthenticationManager;importorg.springframework.security.config.annotation.SecurityConfigurerAdapter;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.web.DefaultSecurityFilterChain;importorg.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;importorg.springframework.stereotype.Component;/***自定义的用户名密码认证配置类*@authorgeff*/@ComponentpublicclassAuthConfigextendsSecurityConfigurerAdapter&lt;DefaultSecurityFilterChain,HttpSecurity&gt;{@AutowiredAuthProviderprovider;@AutowiredAuthSuccessHandlerauthSuccessHandler;@AutowiredAuthFailureHandlerauthFailureHandler;@Overridepublicvoidconfigure(HttpSecurityhttp){AuthFilterauthFilter=newAuthFilter();/**自定义用户认证处理逻辑时,需要指定AuthenticationManager,否则无法认证*/authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));/**指定自定义的认证成功和失败的处理器*/authFilter.setAuthenticationSuccessHandler(authSuccessHandler);authFilter.setAuthenticationFailureHandler(authFailureHandler);/**把自定义的用户名密码认证过滤器和处理器添加到UsernamePasswordAuthenticationFilter过滤器之前*/http.authenticationProvider(provider).addFilterBefore(authFilter,UsernamePasswordAuthenticationFilter.class);}}7.2SpringSecurity核心配置类packagecn.coralcloud.security.config;importcn.coralcloud.security.component.AuthAccessDeniedHandler;importcn.coralcloud.security.component.AuthConfig;importcn.coralcloud.security.component.Md5SaltPasswordEncoder;importcn.coralcloud.security.model.Response;importcn.coralcloud.security.service.UserDetailsServiceImpl;importcom.fasterxml.jackson.databind.ObjectMapper;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.crypto.password.PasswordEncoder;importjava.io.PrintWriter;/***@authorgeff*/@Configuration@EnableWebSecurity@Slf4jpublicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{@AutowiredprivateAuthConfigauthConfig;@AutowiredprivateAuthAccessDeniedHandleraccessDeniedHandler;@Overrideprotectedvoidconfigure(HttpSecurityhttpSecurity)throwsException{httpSecurity.apply(authConfig).and().authorizeRequests().antMatchers(&quot;/static/**&quot;,&quot;/api/user/login&quot;).permitAll().anyRequest().authenticated().and().csrf().disable();httpSecurity.exceptionHandling().accessDeniedHandler(accessDeniedHandler).authenticationEntryPoint((request,response,e)-&gt;{response.setContentType(&quot;application/json;charset=utf-8&quot;);PrintWriterout=response.getWriter();Responseres=Response.fail(-14,&quot;会话超时,请重新登录!&quot;);out.write(newObjectMapper().writeValueAsString(res));out.flush();out.close();});}@AutowiredpublicvoidconfigureGlobal(AuthenticationManagerBuilderauth)throwsException{auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewMd5SaltPasswordEncoder();}@Bean@OverridepublicUserDetailsServiceuserDetailsService(){returnnewUserDetailsServiceImpl();}}本文通过自定义AuthenticationEntryPoint来解决匿名用户访问无权限资源时的异常

Tags: JAVA
工作日志
【基于ELK搭建日志分析系统】- FileBeat安装

采集节点主要安装Filebeat组件即可,Filebeat可以很简单运行在Windows/Linux操作系统上,不需要其余环境。下载Filebeat压缩包:https://www.elastic.co/downloads/past-releases/filebeat-6-2-4根据操作系统选择对应的版本下载解压修改目录下filebeat.yml文件,具体配置下图:上图为filebeat的输入端配置,指定监听文件上图为filebeat的输出端配置,指定输出到本机的Logstash中Filebeat启动(cd$FILEBEAT_HOME):./filebeat-e-cfilebeat.yml后台启动可使用nohup命令nohup./filebeat-e-cfilebeat.yml-d&quot;publish&quot;&gt;logs/filebeat.log2&gt;&amp;1&amp;

转载分享
Spring Boot 集成Sharding-jdbc + Mybatis-Plus实现分库分表

一、Sharding-jdbc简介Sharding-jdbc是开源的数据库操作中间件;定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。官方文档地址:https://shardingsphere.apache.org/document/current/cn/overview/本文demo实现了分库分表功能。作者能力有限,如有错误,欢迎各位在评论中指出。不胜感激!二、项目结构首先创建一个一般的Springboot项目,项目采用三层架构,结构图如下:项目目录结构图POM.xml文件如下:&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;projectxmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;&lt;parent&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;&lt;version&gt;2.1.6.RELEASE&lt;/version&gt;&lt;relativePath/&gt;&lt;!--lookupparentfromrepository--&gt;&lt;/parent&gt;&lt;groupId&gt;com.macky&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-shardingjdbc&lt;/artifactId&gt;&lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;&lt;name&gt;spring-boot-shardingjdbc&lt;/name&gt;&lt;description&gt;Demoprojectforspring-boot-shardingjdbc&lt;/description&gt;&lt;properties&gt;&lt;java.version&gt;1.8&lt;/java.version&gt;&lt;/properties&gt;&lt;dependencies&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;&lt;/dependency&gt;&lt;dependency&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;&lt;scope&gt;test&lt;/scope&gt;&lt;/dependency&gt;&lt;!--mysql--&gt;&lt;dependency&gt;&lt;groupId&gt;mysql&lt;/groupId&gt;&lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;&lt;scope&gt;runtime&lt;/scope&gt;&lt;/dependency&gt;&lt;!--Mybatis-Plus--&gt;&lt;dependency&gt;&lt;groupId&gt;com.baomidou&lt;/groupId&gt;&lt;artifactId&gt;mybatis-plus-boot-starter&lt;/artifactId&gt;&lt;version&gt;3.1.1&lt;/version&gt;&lt;/dependency&gt;&lt;!--shardingspherestart--&gt;&lt;!--forspringboot--&gt;&lt;dependency&gt;&lt;groupId&gt;io.shardingsphere&lt;/groupId&gt;&lt;artifactId&gt;sharding-jdbc-spring-boot-starter&lt;/artifactId&gt;&lt;version&gt;3.1.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--forspringnamespace--&gt;&lt;dependency&gt;&lt;groupId&gt;io.shardingsphere&lt;/groupId&gt;&lt;artifactId&gt;sharding-jdbc-spring-namespace&lt;/artifactId&gt;&lt;version&gt;3.1.0&lt;/version&gt;&lt;/dependency&gt;&lt;!--shardingsphereend--&gt;&lt;!--lombok--&gt;&lt;dependency&gt;&lt;groupId&gt;org.projectlombok&lt;/groupId&gt;&lt;artifactId&gt;lombok&lt;/artifactId&gt;&lt;/dependency&gt;&lt;/dependencies&gt;&lt;build&gt;&lt;plugins&gt;&lt;plugin&gt;&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;&lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;&lt;/plugin&gt;&lt;/plugins&gt;&lt;/build&gt;&lt;/project&gt;实体类以书本为例packagecom.macky.springbootshardingjdbc.entity;importcom.baomidou.mybatisplus.annotation.TableName;importcom.baomidou.mybatisplus.extension.activerecord.Model;importgroovy.transform.EqualsAndHashCode;importlombok.Data;importlombok.experimental.Accessors;/***@authorMacky*@TitleclassBook*@Description:书籍是实体类*@date2019/7/1315:23*/@Data@EqualsAndHashCode(callSuper=true)@Accessors(chain=true)@TableName(&quot;book&quot;)publicclassBookextendsModel&lt;Book&gt;{privateintid;privateStringname;privateintcount;}开放保存和查询两个接口,代码如下:packagecom.macky.springbootshardingjdbc.controller;importcom.macky.springbootshardingjdbc.entity.Book;importcom.macky.springbootshardingjdbc.service.BookService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.*;importjava.util.List;/***@authorMacky*@TitleclassBookController*@Description:TODO*@date2019/7/1220:53*/@RestControllerpublicclassBookController{@AutowiredBookServicebookService;@RequestMapping(value=&quot;/book&quot;,method=RequestMethod.GET)publicList&lt;Book&gt;getItems(){returnbookService.getBookList();}@RequestMapping(value=&quot;/book&quot;,method=RequestMethod.POST)publicBooleansaveItem(Bookbook){returnbookService.save(book);}}BookServiceImpl.javapackagecom.macky.springbootshardingjdbc.service.impl;importcom.baomidou.mybatisplus.core.toolkit.Wrappers;importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;importcom.macky.springbootshardingjdbc.entity.Book;importcom.macky.springbootshardingjdbc.mapper.BookMapper;importcom.macky.springbootshardingjdbc.service.BookService;importorg.springframework.stereotype.Service;importjava.util.List;/***@authorMacky*@TitleclassBookServiceImpl*@Description:TODO*@date2019/7/1220:47*/@ServicepublicclassBookServiceImplextendsServiceImpl&lt;BookMapper,Book&gt;implementsBookService{@OverridepublicList&lt;Book&gt;getBookList(){returnbaseMapper.selectList(Wrappers.&lt;Book&gt;lambdaQuery());}@Overridepublicbooleansave(Bookbook){returnsuper.save(book);}}BookMapper.javapackagecom.macky.springbootshardingjdbc.mapper;importcom.baomidou.mybatisplus.core.mapper.BaseMapper;importcom.macky.springbootshardingjdbc.entity.Book;/***@authorMacky*@TitleclassBookMapper*@Description:TODO*@date2019/7/1220:46*/publicinterfaceBookMapperextendsBaseMapper&lt;Book&gt;{}创建数据库表,DDL语句如下:#创建数据库表数据CREATEDATABASEIFNOTEXISTS`db0`;USE`db0`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;CREATEDATABASEIFNOTEXISTS`db1`;USE`db1`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;CREATEDATABASEIFNOTEXISTS`db2`;USE`db2`;DROPTABLEIFEXISTS`book_0`;CREATETABLE`book_0`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;DROPTABLEIFEXISTS`book_1`;CREATETABLE`book_1`(`id`INT(11)NOTNULL,`name`VARCHAR(255)DEFAULTNULL,`count`INT(11)DEFAULTNULL,PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8mb4;配置分库分表策略application.properties:#数据源db0,db1,db2sharding.jdbc.datasource.names=db0,db1,db2#第一个数据库sharding.jdbc.datasource.db0.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db0.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db0.jdbc-url=jdbc:mysql://localhost:3306/db0?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db0.username=rootsharding.jdbc.datasource.db0.password=Aa123456#第二个数据库sharding.jdbc.datasource.db1.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db1.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db1.jdbc-url=jdbc:mysql://localhost:3306/db1?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db1.username=rootsharding.jdbc.datasource.db1.password=Aa123456#第三个数据库sharding.jdbc.datasource.db2.type=com.zaxxer.hikari.HikariDataSourcesharding.jdbc.datasource.db2.driver-class-name=com.mysql.cj.jdbc.Driversharding.jdbc.datasource.db2.jdbc-url=jdbc:mysql://localhost:3306/db2?useUnicode=true&amp;useJDBCCompliantTimezoneShift=true&amp;useLegacyDatetimeCode=false&amp;serverTimezone=UTCsharding.jdbc.datasource.db2.username=rootsharding.jdbc.datasource.db2.password=Aa123456#水平拆分的数据库(表)配置分库+分表策略行表达式分片策略#分库策略sharding.jdbc.config.sharding.default-database-strategy.inline.sharding-column=idsharding.jdbc.config.sharding.default-database-strategy.inline.algorithm-expression=db$-&gt;{id%3}#分表策略其中book为逻辑表分表主要取决于id行sharding.jdbc.config.sharding.tables.book.actual-data-nodes=db$-&gt;{0..2}.book_$-&gt;{0..2}sharding.jdbc.config.sharding.tables.book.table-strategy.inline.sharding-column=count#分片算法表达式sharding.jdbc.config.sharding.tables.book.table-strategy.inline.algorithm-expression=book_$-&gt;{count%3}#主键UUID18位数如果是分布式还要进行一个设置防止主键重复#sharding.jdbc.config.sharding.tables.user.key-generator-column-name=id#打印执行的数据库以及语句sharding.jdbc.config.props..sql.show=truespring.main.allow-bean-definition-overriding=true#读写分离sharding.jdbc.datasource.dsmaster=接口测试使用postman示例:GET请求------&gt;http://localhost:8080/bookPOST请求:-------&gt;http://localhost:8080/book?id=1&amp;name=java编程思想&amp;count=8demo的github地址:https://github.com/Macky-He/spring-boot--shardingsphere-examples如各位觉得有帮助的话,还请给个star鼓励鼓励博主,谢谢!三、总结分库分表实现按照官方文档做一个demo是第一步,如需深入还需要研究源码,研究架构,研究思想;此文仅作为入门demo搭建指南,如需深入理解,还请移步至官方文档。参考资料1.官方文档:https://shardingsphere.apache.org/document/current/cn/manual/sharding-jdbc/usage/sharding/

Tags: JAVA
开发项目专栏
SpringBoot+Thymleaf项目初入(五) - 图片验证码

1.验证码工具类1.新建包utils2.新建VerifyCodeUtil类packagecn.coralcloud.ims.utils;importjavax.imageio.ImageIO;importjava.awt.*;importjava.awt.geom.AffineTransform;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.FileOutputStream;importjava.io.IOException;importjava.io.OutputStream;importjava.util.Arrays;importjava.util.Random;/***@authorc-geff*/publicclassVerifyCodeUtil{/***使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符*/privatestaticfinalStringVERIFY_CODES=&quot;23456789ABCDEFGHJKLMNPQRSTUVWXYZ&quot;;privatestaticRandomrandom=newRandom();/***使用系统默认字符源生成验证码*@paramverifySize验证码长度*@return*/publicstaticStringgenerateVerifyCode(intverifySize){returngenerateVerifyCode(verifySize,VERIFY_CODES);}/***使用指定源生成验证码*@paramverifySize验证码长度*@paramsources验证码字符源*@return*/publicstaticStringgenerateVerifyCode(intverifySize,Stringsources){if(sources==null||sources.length()==0){sources=VERIFY_CODES;}intcodesLen=sources.length();Randomrand=newRandom(System.currentTimeMillis());StringBuilderverifyCode=newStringBuilder(verifySize);for(inti=0;i&lt;verifySize;i++){verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)));}returnverifyCode.toString();}/***生成随机验证码文件,并返回验证码值*@paramw*@paramh*@paramoutputFile*@paramverifySize*@return*@throwsIOException*/publicstaticStringoutputVerifyImage(intw,inth,FileoutputFile,intverifySize)throwsIOException{StringverifyCode=generateVerifyCode(verifySize);outputImage(w,h,outputFile,verifyCode);returnverifyCode;}/***输出随机验证码图片流,并返回验证码值*@paramw*@paramh*@paramos*@paramverifySize*@return*@throwsIOException*/publicstaticStringoutputVerifyImage(intw,inth,OutputStreamos,intverifySize)throwsIOException{StringverifyCode=generateVerifyCode(verifySize);outputImage(w,h,os,verifyCode);returnverifyCode;}/***生成指定验证码图像文件*@paramw*@paramh*@paramoutputFile*@paramcode*@throwsIOException*/publicstaticvoidoutputImage(intw,inth,FileoutputFile,Stringcode)throwsIOException{if(outputFile==null){return;}Filedir=outputFile.getParentFile();if(!dir.exists()){dir.mkdirs();}try{outputFile.createNewFile();FileOutputStreamfos=newFileOutputStream(outputFile);outputImage(w,h,fos,code);fos.close();}catch(IOExceptione){throwe;}}/***输出指定验证码图片流*@paramw*@paramh*@paramos*@paramcode*@throwsIOException*/publicstaticvoidoutputImage(intw,inth,OutputStreamos,Stringcode)throwsIOException{intverifySize=code.length();BufferedImageimage=newBufferedImage(w,h,BufferedImage.TYPE_INT_RGB);Randomrand=newRandom();Graphics2Dg2=image.createGraphics();g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);Color[]colors=newColor[5];Color[]colorSpaces=newColor[]{Color.WHITE,Color.CYAN,Color.GRAY,Color.LIGHT_GRAY,Color.MAGENTA,Color.ORANGE,Color.PINK,Color.YELLOW};float[]fractions=newfloat[colors.length];for(inti=0;i&lt;colors.length;i++){colors[i]=colorSpaces[rand.nextInt(colorSpaces.length)];fractions[i]=rand.nextFloat();}Arrays.sort(fractions);//设置边框色g2.setColor(Color.GRAY);g2.fillRect(0,0,w,h);Colorc=getRandColor(200,250);//设置背景色g2.setColor(c);g2.fillRect(0,2,w,h-4);//绘制干扰线Randomrandom=newRandom();//设置线条的颜色g2.setColor(getRandColor(160,200));intloop=20;for(inti=0;i&lt;loop;i++){intx=random.nextInt(w-1);inty=random.nextInt(h-1);intxl=random.nextInt(6)+1;intyl=random.nextInt(12)+1;g2.drawLine(x,y,x+xl+40,y+yl+20);}//添加噪点//噪声率floatyawpRate=0.05f;intarea=(int)(yawpRate*w*h);for(inti=0;i&lt;area;i++){intx=random.nextInt(w);inty=random.nextInt(h);intrgb=getRandomIntColor();image.setRGB(x,y,rgb);}//使图片扭曲shear(g2,w,h,c);g2.setColor(getRandColor(100,160));intfontSize=h-4;Fontfont=newFont(&quot;Algerian&quot;,Font.ITALIC,fontSize);g2.setFont(font);char[]chars=code.toCharArray();for(inti=0;i&lt;verifySize;i++){AffineTransformaffine=newAffineTransform();affine.setToRotation(Math.PI/4*rand.nextDouble()*(rand.nextBoolean()?1:-1),(w/verifySize)*i+fontSize/2,h/2);g2.setTransform(affine);g2.drawChars(chars,i,1,((w-10)/verifySize)*i+5,h/2+fontSize/2-10);}g2.dispose();ImageIO.write(image,&quot;jpg&quot;,os);}privatestaticfinalIntegerCOLOR_MAX_INT=255;privatestaticColorgetRandColor(intfc,intbc){if(fc&gt;COLOR_MAX_INT){fc=COLOR_MAX_INT;}if(bc&gt;COLOR_MAX_INT){bc=COLOR_MAX_INT;}intr=fc+random.nextInt(bc-fc);intg=fc+random.nextInt(bc-fc);intb=fc+random.nextInt(bc-fc);returnnewColor(r,g,b);}privatestaticintgetRandomIntColor(){int[]rgb=getRandomRgb();intcolor=0;for(intc:rgb){color=color&lt;&lt;8;color=color|c;}returncolor;}privatestaticint[]getRandomRgb(){int[]rgb=newint[3];intloop=3;for(inti=0;i&lt;loop;i++){rgb[i]=random.nextInt(255);}returnrgb;}privatestaticvoidshear(Graphicsg,intw1,inth1,Colorcolor){shearx(g,w1,h1,color);sheary(g,w1,h1,color);}privatestaticvoidshearx(Graphicsg,intw1,inth1,Colorcolor){intperiod=random.nextInt(2);booleanborderGap=true;intframes=1;intphase=random.nextInt(2);for(inti=0;i&lt;h1;i++){doubled=(double)(period&gt;&gt;1)*Math.sin((double)i/(double)period+(6.2831853071795862D*(double)phase)/(double)frames);g.copyArea(0,i,w1,1,(int)d,0);if(borderGap){g.setColor(color);g.drawLine((int)d,i,0,i);g.drawLine((int)d+w1,i,w1,i);}}}privatestaticvoidsheary(Graphicsg,intw1,inth1,Colorcolor){intperiod=random.nextInt(40)+10;booleanborderGap=true;intframes=20;intphase=7;for(inti=0;i&lt;w1;i++){doubled=(double)(period&gt;&gt;1)*Math.sin((double)i/(double)period+(6.2831853071795862D*(double)phase)/(double)frames);g.copyArea(i,0,1,h1,0,(int)d);if(borderGap){g.setColor(color);g.drawLine(i,(int)d,i,0);g.drawLine(i,(int)d+h1,i,h1);}}}}2.UserController新增验证码接口2.1新增captcha方法@GetMapping(&quot;/captcha&quot;)publicvoidcaptcha(HttpServletResponseresponse,HttpSessionsession){Stringcode=VerifyCodeUtil.generateVerifyCode(4);session.setAttribute(SessionKey.ADMIN_CAPTCHA_KEY,code);try{VerifyCodeUtil.outputImage(150,50,response.getOutputStream(),code);}catch(IOExceptione){e.printStackTrace();}}2.2修改login方法,增加验证下方为最终的登录验证方法:@PostMapping(&quot;/login&quot;)publicModelAndViewlogin(Stringemail,Stringpassword,Stringcaptcha,HttpSessionsession){StringsessionCaptcha=(String)session.getAttribute(SessionKey.ADMIN_CAPTCHA_KEY);ModelAndViewview=newModelAndView();view.setViewName(&quot;user/login&quot;);//将email和password写回到页面,使得登录失败时输入的账号密码不会丢失view.addObject(&quot;email&quot;,email);view.addObject(&quot;password&quot;,password);if(StringUtils.isEmpty(captcha)||!Objects.equals(captcha,sessionCaptcha)){view.addObject(&quot;errmsg&quot;,&quot;验证码错误!&quot;);returnview;}Useruser=userService.login(email,password);if(user!=null){session.setAttribute(SessionKey.ADMIN_USER_KEY,user);view.setViewName(&quot;redirect:/index&quot;);returnview;}view.addObject(&quot;errmsg&quot;,&quot;用户名或密码错误!&quot;);returnview;}注:此处将Session的Key值通过一个常量类SessionKey保存2.3SessionKey类在utils包下新建SessionKey类packagecn.coralcloud.ims.utils;/***@authorc-geff*@nameSessionKey*@description*@date2020-11-0410:12*/publicclassSessionKey{publicstaticfinalStringADMIN_USER_KEY=&quot;AdminUserKey&quot;;publicstaticfinalStringADMIN_CAPTCHA_KEY=&quot;AdminLoginCaptchaCode&quot;;}3.修改login.ftl3.1引入jquery下载jqeury文件jquery.min.js在/static/js文件夹下新建jquery文件夹,将jquery.min.js复制到文件夹下修改login.ftl文件,最终login.ftl内容为下:&lt;!doctypehtml&gt;&lt;htmllang=&quot;en&quot;xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;&lt;head&gt;&lt;metacharset=&quot;UTF-8&quot;&gt;&lt;metaname=&quot;viewport&quot;content=&quot;width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0&quot;&gt;&lt;metahttp-equiv=&quot;X-UA-Compatible&quot;content=&quot;ie=edge&quot;&gt;&lt;linktype=&quot;text/css&quot;rel=&quot;stylesheet&quot;href=&quot;/static/css/main.css&quot;&gt;&lt;linktype=&quot;text/css&quot;rel=&quot;stylesheet&quot;href=&quot;/static/css/login.css&quot;&gt;&lt;scriptsrc=&quot;/static/js/jquery/jquery.min.js&quot;type=&quot;application/javascript&quot;&gt;&lt;/script&gt;&lt;title&gt;用户登录&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;divclass=&quot;login-container&quot;&gt;&lt;formaction=&quot;/user/login&quot;method=&quot;post&quot;&gt;&lt;div&gt;&lt;h3&gt;用户登录&lt;/h3&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;email&quot;&gt;登录邮箱&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;email&quot;autocomplete=&quot;off&quot;th:value=&quot;${email}&quot;placeholder=&quot;请输入邮箱&quot;type=&quot;text&quot;name=&quot;email&quot;&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;password&quot;&gt;登录密码&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;password&quot;autocomplete=&quot;new-password&quot;th:value=&quot;${password}&quot;placeholder=&quot;请输入密码&quot;type=&quot;password&quot;name=&quot;password&quot;&gt;&lt;/div&gt;&lt;divclass=&quot;ims-form-item&quot;&gt;&lt;labelclass=&quot;ims-form-label&quot;for=&quot;captcha&quot;&gt;图片验证&lt;/label&gt;&lt;inputclass=&quot;ims-form-input&quot;id=&quot;captcha&quot;type=&quot;text&quot;placeholder=&quot;请输入图片验证码&quot;name=&quot;captcha&quot;&gt;&lt;imgsrc=&quot;/user/captcha&quot;class=&quot;captcha&quot;alt=&quot;图片验证码&quot;&gt;&lt;/div&gt;&lt;pclass=&quot;errormsg&quot;th:if=&quot;${errmsg}!=null&quot;th:text=&quot;${errmsg}&quot;&gt;&lt;/p&gt;&lt;div&gt;&lt;buttontype=&quot;submit&quot;class=&quot;ims-button&quot;&gt;登录&lt;/button&gt;&lt;/div&gt;&lt;/form&gt;&lt;/div&gt;&lt;/body&gt;&lt;scripttype=&quot;application/javascript&quot;&gt;$(document).ready(function(){$(&#39;img.captcha&#39;).click(function(){$(this).attr(&quot;src&quot;,&quot;/user/captcha?_=&quot;+newDate().getTime())})})&lt;/script&gt;&lt;/html&gt;注,login.ftl变更如下:1.head新增引入jquery.min.js2.input新增读取前面保存的email和password3.新增图片验证码输入框及图片4.新建图片点击监听事件,更换图片验证码4.修改样式login.css#captcha{width:100px;border-top-right-radius:0;border-bottom-right-radius:0;}.captcha{width:1px;flex:1;height:42px;cursor:pointer;border-top-right-radius:4px;border-bottom-right-radius:4px;}5.最终效果

个人随笔
Linux安装Apache-2.4.34

Linux离线安装Apache-2.41.系统环境信息系统版本:Linux2.6.32-696.el6.x86_64操作系统:Centos6.92.前置准备Apache-2.4编译安装依赖apr、apr-util、pcre,所以安装前需要先下载好四个离线安装包,安装包下载地址:apr-1.5.2apr-util-1.5.4pcre-8.42httpd-2.4.343.编译安装aprcd/home/softwaretar-zxvfapr-1.5.2.tar.gzcdapr-1.5.2./configure--prefix=/usr/local/apr-1.5.2make&amp;&amp;makeinstall4.编译安装apr-utilcd/home/softwaretar-zxvfapr-util-1.5.4.tar.gzcdapr-util-1.5.4./configure--prefix=/usr/local/apr-util-1.5.4--with-apr=/usr/local/apr-1.5.2make&amp;&amp;makeinstall5.编译安装pcrecd/home/softwaretar-zxvfpcre-8.42.tar.gzcdpcre-8.42./configure--prefix=/usr/local/pcre-8.42make&amp;&amp;makeinstall6.编译安装httpdcd/home/softwaretar-zxvfhttpd-2.4.34.tar.gzcdhttpd-2.4.34./configure--prefix=/usr/local/httpd-2.4.34--with-apr=/usr/local/apr-1.5.2--with-apr-util=/usr/local/apr-util-1.5.4--with-pcre=/usr/local/pcre-8.42make&amp;&amp;makeinstall7.配置httpd.confapache编译安装完成后,配置文件地址在/usr/local/apache-2.4.34/conf/httpd.conf修改启动端口为8080Listen80818.启动Apache创建软链接:ln-s/usr/local/apache-2.4.34/bin/apachectl/usr/bin/apachectl启动:/usr/bin/apachectl

Tags: Linux
开发项目专栏
SpringBoot+Thymleaf项目初入(三) - 用户登录

1.新建数据库表ims-learn.user2.Model包下新建User类注:因为之前初始化项目时添加了lombok依赖,所以model类可以不用写setter/getter,直接使用@Data注解替代3.编写UserDao接口类4.UserMapper.xml&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;!DOCTYPEmapperPUBLIC&quot;-//mybatis.org//DTDMapper3.0//EN&quot;&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;&lt;mappernamespace=&quot;cn.coralcloud.ims.dao.UserDao&quot;&gt;&lt;resultMapid=&quot;userResultMap&quot;type=&quot;User&quot;&gt;&lt;idproperty=&quot;id&quot;column=&quot;id&quot;/&gt;&lt;resultproperty=&quot;createTime&quot;column=&quot;create_time&quot;/&gt;&lt;resultproperty=&quot;updateTime&quot;column=&quot;update_time&quot;/&gt;&lt;/resultMap&gt;&lt;selectid=&quot;login&quot;resultMap=&quot;userResultMap&quot;&gt;select*fromuserwhereemail=#{email}andpassword=#{password}&lt;/select&gt;&lt;/mapper&gt;5.mybatis.xml编写mybatis基础配置,新建resources/mybatis/mybatis.xml&lt;?xmlversion=&quot;1.0&quot;encoding=&quot;UTF-8&quot;?&gt;&lt;!DOCTYPEconfigurationPUBLIC&quot;-//mybatis.org//DTDConfig3.0//EN&quot;&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;&gt;&lt;configuration&gt;&lt;settings&gt;&lt;settingname=&quot;logImpl&quot;value=&quot;STDOUT_LOGGING&quot;/&gt;&lt;settingname=&quot;lazyLoadingEnabled&quot;value=&quot;true&quot;/&gt;&lt;settingname=&quot;aggressiveLazyLoading&quot;value=&quot;false&quot;/&gt;&lt;settingname=&quot;lazyLoadTriggerMethods&quot;value=&quot;&quot;/&gt;&lt;/settings&gt;&lt;/configuration&gt;6.UserService.java7.UserController.javapackagecn.coralcloud.ims.controller;importcn.coralcloud.ims.model.User;importcn.coralcloud.ims.service.UserService;importcom.sun.org.apache.xpath.internal.operations.Mod;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Controller;importorg.springframework.ui.Model;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.servlet.ModelAndView;importorg.springframework.web.servlet.View;importjavax.servlet.http.HttpSession;/***@authorc-geff*@nameUserController*@description*@date2020-11-0310:45*/@Controller@RequestMapping(&quot;/user&quot;)publicclassUserController{@AutowiredprivateUserServiceuserService;@PostMapping(&quot;/login&quot;)publicModelAndViewlogin(Stringemail,Stringpassword,HttpSessionsession){ModelAndViewview=newModelAndView();view.setViewName(&quot;user/login&quot;);Useruser=userService.login(email,password);if(user!=null){session.setAttribute(&quot;AdminUserKey&quot;,user);view.setViewName(&quot;redirect:/index&quot;);returnview;}view.addObject(&quot;errmsg&quot;,&quot;用户名或密码错误!&quot;);returnview;}@GetMapping(&quot;/login&quot;)publicStringlogin(){return&quot;user/login&quot;;}}8.login.ftl9.IndexController改造10.index.ftl改造,展示用户登录后的用户名11.数据库添加一条用户数据,然后在/user/login页面登录测试