前言:
上一篇介绍了Shiro整合SpringBoot实现了登录功能,不过登录时用户信息都是写死在程序里的,也没有提供注册的功能,真实场景中肯定是不能这么干,这篇文章就是总结我们如何模拟真实场景中的注册功能,然后根据注册信息完成登录,且存入数据库的密码是经过MD5+盐+hash散列加密以后的密码。
一.整合过程
上一篇文章已经介绍了Shiro整合SpingBoot的过程,这一篇就接着上一篇的内容去整合Mybatis+Mysql了,若是没有数据库,可以根据下面这篇先去装上一个mysql的数据库。看完女朋友都能装数据库的教程
1.导入Mybatis与Mysql相关的依赖
如下所示,首先我们需要引入Mybatis的启动器、Mysql的驱动类、以及druid的数据源。
<!--引入mybatis的依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency> <!--引入连接mysql的驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.38</version> </dependency> <!--引入数据源连接池依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.19</version> </dependency>
2.配置application.properties文件
我们需要配置数据库的参数和Mybatis的别名以及mapper文件路径,如下所示
# 数据库相关配置 #连接池 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource #驱动类 spring.datasource.driver-class-name=com.mysql.jdbc.Driver #数据库地址 spring.datasource.url=jdbc:mysql://192.168.150.130:3306/shiro?characterEncoding=UTF-8&useSSL=false #数据库用户名与密码 spring.datasource.username=root spring.datasource.password=super # mybatis 别名配置,配置该参数mapper中才认识自定义类型 mybatis.type-aliases-package=com.example.demo5.entry # 配置了该参数才能找到我们写mapper文件 mybatis.mapper-locations=classpath:com/example/demo5/mapper/*.xml
3.提供Service接口及其实现类,以及dao接口
我们需要为用户信息提供服务层接口以及数据处理层的接口,如下:
1.Service层接口
服务层接口,两个方法就够了,一个用户注册时插入到表中,一个用户登录时查询用户信息。
public interface ShiroUserService { void insertUser(ShiroUser shiroUser); ShiroUser queryUser(String username); }
2.Service层实现类
@Service @Transactional public class ShiroUserServiceImpl implements ShiroUserService { @Autowired private ShiroUserDao shiroUserDao; @Override public void insertUser(ShiroUser shiroUser) { //使用MD5+盐+hash散列进行对密码加密 String salt = SaltUttil.getSalt(10); Md5Hash md5Hash = new Md5Hash(shiroUser.getPassword(),salt ,2048); shiroUser.setPassword(md5Hash.toString()); shiroUser.setSalt(salt); shiroUserDao.insertUser(shiroUser); } @Override public ShiroUser queryUser(String username) { ShiroUser shiroUser = shiroUserDao.queryUser(username); return shiroUser; } }
3.dao接口
我们需要为dao接口提供一个Mapper注解,或者在启动类上加入MapperScan注解也可以。两者选其一即可。
//@Mapper public interface ShiroUserDao { void insertUser(ShiroUser shiroUser); ShiroUser queryUser(String username); }
提供一个Salt的生成工具类
public class SaltUttil { public static String getSalt(int n){ char[] chars = "ABCDEFGHIJKLMN0PQRSTUVWXYZabcdefghijklmn0pqrstuvwxyz0123456789~!@#$%^&*;'.,".toCharArray(); StringBuilder stringBuilder = new StringBuilder(); for(int i=0;i<n;i++){ double random = Math.random()*75; char ch = chars[(int)(Math.random()*75)]; stringBuilder.append(ch); } return stringBuilder.toString(); }
4.根据dao方法实现mapper.xml文件
每个dao方法都要映射到一个mapper文件内的一段sql。我们前面使用的Mapper标签就是为了根据mapper文件中的sql自动生成dao的实现类。我们首先需要在resoures文件夹下创建一个目录,目录名随意,这里需要与application.properties中配置的mapper路径保持一致。创建目录时,若是多级目录注意不能使用点隔开,应使用/隔开。
mapper.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.demo5.dao.ShiroUserDao"> <sql id="shiro_user_fields"> oid,username,password,salt </sql> <insert id="insertUser" parameterType="ShiroUser" useGeneratedKeys="true" keyProperty="oid"> insert into shiro_user values (#{oid},#{username},#{password},#{salt}) </insert> <select id="queryUser" parameterType="java.lang.String" resultType="ShiroUser"> select <include refid="shiro_user_fields"/> from shiro_user where 1=1 <if test="username != null and username != ''"> and username = #{username} </if> </select> </mapper>
这也是一个标准的mapper.xml文件的模板,可以当做模板使用,该文件需要注意的就是,命名空间需要是dao的全限定名。
5.整合完成
到这里其实就整合完成了。如果整合过程中碰到任何问题请参考这篇文章,SpringBoot整合MyBatis,service层中的insert方法对应的就是注册功能,可以看到调用dao方法时,传入的是MD5+盐+hash散列处理后的密码,这样我们在数据库存储的就是加密后的密码了。
二.实现注册与登录
完整的登录肯定是从注册开始,注册就必须得先有个注册页面。
1.提供注册页面。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>注册</title> <style type="text/css"> *{margin: 0;padding: 0;} form{margin: 0 auto;padding:15px; width: 300px;height:300px;text-align: center;} #submit{padding: 10px} #submit input{width: 50px;height: 24px;} </style> </head> <body> <h1>注册页</h1> <form action="${pageContext.request.contextPath}/user/register" method="post"> 用户名:<input type="text" name ="username"/><br/> 密 码 :<input type="text" name ="password"/><br/> <input type="submit" value="立即注册"><br/> </form> </body> </html>
2.提供登录接口
然后需要提供一个接口register供调用,存储用户信息,register接口如下,这里我们直接调用Service层接口即可。
@PostMapping("/register") public String register(ShiroUser shiroUser){ try { shiroUserService.insertUser(shiroUser); } catch (Exception e) { e.printStackTrace(); return "redirect:/register.jsp"; } return "redirect:/login.jsp"; }
这样我们就可以完成整个注册的流程了。
3.测试注册
点击立即注册后,我们查看数据库中有无数据,然后我们发现注册正常,数据以及有了,这证明我们写的注册功能没有问题。
4.Realm中查询数据库用户信息
既然注册成功了,我们就需要为登录做准备了,我们知道认证予授权的数据均是来自Realm,那么我们的Realm自然就需要调用Service的方法来查询数据库信息了。实现如下:
public class FirstRealm extends AuthorizingRealm { @Autowired ShiroUserService shiroUserService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)authenticationToken; ShiroUser shiroUser = shiroUserService.queryUser(usernamePasswordToken.getUsername()); if(shiroUser!=null){ return new SimpleAuthenticationInfo(shiroUser.getUsername(),shiroUser.getPassword(),ByteSource.Util.bytes(shiroUser.getSalt()),this.getName()); } return null; } }
5.为自定义Realm设置密码匹配器
因为使用了MD5+盐+hash散列的方式进行加密,那么我们需要为Realm重新设置密码匹配器,并告诉他散列的次数。
@Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager); Map<String,String> map = new HashMap<>(); map.put("/user/*","anon");//表示该资源无需认证授权,无需授权的应该写在上面 map.put("/user/logout","anon");//表示该资源无需认证授权 map.put("/register.jsp","anon");//表示该资源无需认证授权 map.put("/test","anon");//表示该资源无需认证授权 map.put("/login.jsp","anon");//表示该资源无需认证授权,此处不写是不能正常访问到登录页面的, //但是看的课程上是可以访问到,并且无其他配置,这块如果不加,我这里访问不到登录页,会陷入循环的重定向。 map.put("/**","authc");//表示所有资源都需要经过认证授权 shiroFilterFactoryBean.setFilterChainDefinitionMap(map); //设置授权失败返回的页面 shiroFilterFactoryBean.setLoginUrl("login.jsp");//这也是默认值 return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(FirstRealm firstReaml){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(firstReaml); return defaultWebSecurityManager; } @Bean public FirstRealm getRealm(){ FirstRealm firstRealm = new FirstRealm(); Md5CredentialsMatcher md5CredentialsMatcher = new Md5CredentialsMatcher(); md5CredentialsMatcher.setHashIterations(2048); firstRealm.setCredentialsMatcher(md5CredentialsMatcher); return firstRealm; } }
这样我们就完成了登录所需要的所有条件。
6.测试登录
我们使用我们刚刚注册地秦始皇:qaz1231@#,来进行登录。
然后,登录成功进入到了系统首页,这样我们就完成了Shiro+SpringBoot+Mybatis的整合。
三.总结
前面几篇文章里已经介绍过了MD5+盐+hash散列的过程,所以这篇文章其实旨在整合的过程,代码其实都是旧代码,只是应用在了一个完整的流程中。并没有太多的新意,总结下这个过程,以防忘记,若是能帮助到看到此处的你深感荣幸。