一.导入依赖包

<!--spring boot 默认lettuce连接redis的技术--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--邮件--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!--jdbc依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--前端模板引擎依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--springmvc--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> <!--mybatis依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!--热部署依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!--mysql数据库依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!--简化实体类依赖(set/get)--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--单元测试依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 分页插件 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency> <!-- SpringBoot - MyBatis 逆向工程 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.2</version> <scope>runtime</scope> <optional>true</optional> </dependency> <!--EXCEL--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>net.oschina.likaixuan</groupId> <artifactId>excelutil</artifactId> <version>2.0.2</version> </dependency> <!--对象池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!--连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.0</version> </dependency> <!-- fastjson阿里巴巴jSON处理器 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.13</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.4.0</version> </dependency> <!--thymeleaf对shiro扩展的依赖--> <dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency> <!--jedis依赖包--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <!--redisson--> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.5.4</version> </dependency> <!-- shiro+redis缓存插件 --> <dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis</artifactId> <version>2.4.2.1-RELEASE</version> </dependency> <!-- 整合jsp所需依赖,要是添加了thymeleaf似乎会出问题--> <!--<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency>--> <!--jstl--> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
二.添加配置

#tomcat启动端口 server.port=9090 #server.servlet.context-path=/ #自动重启 spring.devtools.restart.enabled=true #添加额外监听的路径 #spring.devtools.restart.additional-paths=src/main/ #添加忽略目录(排除的路径) #spring.devtools.restart.exclude=/excludepath/ #实时刷新(通知浏览器实时刷新,要下插件) spring.devtools.livereload.enabled=true #mysql配置 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://xx.xx.xx.xx:3306/xxx?useUnicode=true&characterEncoding=utf8 spring.datasource.username = xxx spring.datasource.password = xxxx spring.datasource.max-active=20 spring.datasource.max-idle=8 spring.datasource.min-idle=8 spring.datasource.initial-size=10 #mybatis #把数据库字段的下划线转换成实体类中的驼峰式命名 mybatis.configuration.map-underscore-to-camel-case=true #mybatis表映射文件目录 mybatis.mapper-locations=classpath:mapper/*.xml #springmvc spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp #配置分页插件pagehelper pagehelper.helperDialect=mysql pagehelper.reasonable=true pagehelper.supportMethodsArguments=true pagehelper.params.count=countSql #禁用thymeleaf缓存 spring.thymeleaf.cache=false #配置连接池 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource #druid监控配置 spring.datasource.filters=stat,wall,log4j spring.datasource.dbcp2.min-idle=5 #初始化提供的连接数 spring.datasource.dbcp2.initial-size=5 #最大的连接数 spring.datasource.dbcp2.max-total=5 #等待连接获取的最大超时时间 spring.datasource.dbcp2.max-wait-millis=200 ##rabbitMQ #spring.rabbitmq.host=xx.xx.xx.xx #spring.rabbitmq.port=5672 #spring.rabbitmq.username=user #spring.rabbitmq.password=user ##redis #spring.redis.host=xx.xx.xx.xx #spring.redis.port=6379 #spring.redis.password=xxx #spring.redis.timeout=1000ms ##lettuce最大连接数 #spring.redis.lettuce.pool.max-active=8 ##连接池最大阻塞时间(-1表示没有限制 #spring.redis.lettuce.pool.max-wait=-1ms ##最大空闲连接 #spring.redis.lettuce.pool.max-idle=8 ##最小空闲连接 #spring.redis.lettuce.pool.min-idle=0 # #logging.level.com.seecen=debug
由于springboot还没集成shiro的properties所以要添加配置类,配置类如下(添加配置类之前最好先自定义Realm)
自定义Realm
package com.taotao.shiro; import com.taotao.pojo.User; import com.taotao.service.UserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.ByteSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.HashSet; import java.util.Set; /** * Author: TaoTao 2019/9/14 */ @Component public class UserRealm extends AuthorizingRealm { private static Logger logger = LoggerFactory.getLogger(UserRealm.class); @Autowired private UserService userService; /** * 验证身份信息 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { /** * 验证用户名步骤 * 1.获取令牌中的用户名 * 2.根据用户名去查询是否有该用户 * a.有用户存在 * b.没有,直接抛出异常 * 3.返回一个验证用户名和密码的类对象(封装了md5加密的验证方式) */ logger.info("---------------- 执行 Shiro 凭证认证 ----------------------"); String name = (String) token.getPrincipal(); // 从数据库获取对应用户名密码的用户 User userInfo = userService.findUserByNames(name); if (userInfo != null) { // 用户为禁用状态 if (userInfo.getuState() != 0) { throw new DisabledAccountException("账户已被禁用!"); } logger.info("---------------- Shiro 凭证认证成功 ----------------------"); //md5加密再加盐 //根据表单数据和加密字符串和盐值进行比较验证 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( name, //用户 userInfo.getuPwd(), //密码 ByteSource.Util.bytes(userInfo.getuSal()),//盐值 getName() //realm name ); // 返回给安全管理器,由 securityManager 比对密码的正确性 return authenticationInfo; } throw new UnknownAccountException(); } /** * 获取操作权限 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { logger.info("---------------- 执行 Shiro 权限获取 ---------------------"); //给资源进行授权 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //到数据库查询当前登录用户的授权字符串 //获取当前登录用户 Subject subject = SecurityUtils.getSubject(); User user = (User) subject.getPrincipal(); // User dbUser = userService.find info.addStringPermission(null); logger.info("---------------- Shiro 权限获取成功 ----------------------"); return info; /* String username = (String) principals.getPrimaryPrincipal(); //持久化操作:根据用户名获取角色信息和权限信息 //role: admin manage perms:user:create user :update 模块:权限 Set<String> permissionList = new HashSet<>(); permissionList.add("user:list"); permissionList.add("user:create"); permissionList.add("user:update"); Set<String> roleList = new HashSet<>(); roleList.add("admin"); roleList.add("manager"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(roleList); authorizationInfo.setStringPermissions(permissionList); return authorizationInfo;*/ } } }
添加配置
package com.taotao.shiro; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * Author: TaoTao 2019/9/18 */ @Configuration public class ShiroConfig { /** * Filter工厂 * @param securityManager * @return */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //设置安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String,String> filterMap = new LinkedHashMap<String, String>(); filterMap.put("/user/login","anon"); //指定路径放行 filterMap.put("/user/loginVer","anon"); //设置为登录跳转的页面 shiroFilterFactoryBean.setLoginUrl("/user/login"); //设置未授权跳转页面 shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuth"); //授权过滤 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap); return shiroFilterFactoryBean; } /** * 密码校验规则HashedCredentialsMatcher * 这个类是为了对密码进行编码的 , * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 , * 这个类也负责对form里输入的密码进行编码 * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); // 使用md5 算法进行加密 hashedCredentialsMatcher.setHashAlgorithmName("md5"); // 设置散列次数: 加密次数(这个地方没有盐值也不会影响密码对比) hashedCredentialsMatcher.setHashIterations(3); return hashedCredentialsMatcher; } /** * 自定义Realm */ @Bean(name="userRealms") public UserRealm getRealm(){ UserRealm userRealm = new UserRealm(); //配置加密方式(不然登录失败) userRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return userRealm; } /** * 安全管家 */ @Bean(name = "securityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealms") UserRealm userRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //关联realm securityManager.setRealm(userRealm); return securityManager; } /** * 配置ShiroDialect ,用于thymeleaf和shiro标签配合使用 * @return */ @Bean public ShiroDialect getShiroDialect(){ return new ShiroDialect(); } }
MD5加密加盐
public static void main(String[] args) { //第一:把用户的密码通过md5算法加密 String password = "uuu"; // String md5String = new Md5Hash(password).toString();//不可逆加密算法 // System.out.println("原密码:"+password); // System.out.println("md5加密的密码:"+md5String); // //注册,保存加密后的值 // //登录验证 把表单值动态加密再进行比较uuu // //第二种:加密同时加点盐 // md5String = new Md5Hash(password,"abc").toString(); // System.out.println("md5加密且加盐后的密码:"+md5String); //第三种:加密次数 增加破解成本 String md5String; md5String = new Md5Hash("aaa","9734983ae2c939da48d8bc738d34ffb2",3).toString(); System.out.println("md5加密且加盐,还加加密次数后的密码:"+md5String); // //第四种:随机盐值,配合加密次数 //// String sal = getRandomString(); // md5String = new Md5Hash(password,"abc",3).toString(); // System.out.println("md5加密且随机颜值,还加加密次数后的密码:"+md5String);
总结
在搭建和权限这块基本没什么问题,我所出现的问题是,将密码加密存入数据库后,用户登入失败,在查询资料后发现没有添加 HashedCredentialsMatcher 配置,
然后一直在纠结数据库中的密码使用了盐值进行加密,然后HashedCredentialsMatcher 中并没有添加盐值,而且在该配置类中修改加密次数也可以登入成功(难道这个配置只是在说我加密了哦,求大佬指点)