Shiro整合SpringBoot+Mybatis
前言:
上一篇介绍了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接口
我们需要为用户信息提供服务层接口以及数据处理层的接口,如下:
-
Service层接口
服务层接口,两个方法就够了,一个用户注册时插入到表中,一个用户登录时查询用户信息。public interface ShiroUserService { void insertUser(ShiroUser shiroUser); ShiroUser queryUser(String username); }
-
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; } }
-
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散列的过程,所以这篇文章其实旨在整合的过程,代码其实都是旧代码,只是应用在了一个完整的流程中。并没有太多的新意,总结下这个过程,以防忘记,若是能帮助到看到此处的你深感荣幸。