文章目录
- 6.整合SpringBoot项目实战
- 6.0 整合思路
- 6.1 创建springboot项目
- 6.2 引入shiro依赖
- 6.3 配置shiro环境
- 0.创建配置类
- 1.配置shiroFilterFactoryBean
- 2.配置WebSecurityManager
- 3.创建自定义realm
- 4.配置自定义realm
- 5.编写控制器跳转至index.html
- 6.启动springboot应用访问index
- 7.加入权限控制
- 8.重启项目访问查看
- 6.4 常见过滤器
- 6.5 认证实现
- 1. 在login.jsp中开发认证界面
- 2. 开发controller
- 3.开发realm中返回静态数据(未连接数据库)
- 4.启动项目以realm中定义静态数据进行认证
- 6.6 退出认证
- 1.开发页面退出连接
- 2.开发controller
- 3.修改退出连接访问退出路径
- 4.退出之后访问受限资源立即返回认证界面
- 6.7 MD5、Salt的认证实现
- 1.开发数据库注册
- 0.开发注册界面
- 1.创建数据表结构
- 2.项目引入依赖
- 3.配置application.properties配置文件
- 4.创建entity
- 5.创建DAO接口
- 6.开发mapper配置文件
- 7.开发service接口
- 8.创建salt工具类
- 9.开发service实现类
- 10.开发Controller
- 11.启动项目进行注册
- 2.开发数据库认证
- 0.开发DAO
- 1.开发mapper配置文件
- 2.开发Service接口
- 3.开发Service实现类
- 4.开发在工厂中获取bean对象的工具类
- 5.修改自定义realm
- 6.修改ShiroConfig中realm使用凭证匹配器以及hash散列
- 6.8 授权实现
- 0.页面资源授权
- 1.代码方式授权
- 2.方法调用授权
- 3.授权数据持久化
- 4.创建dao方法
- 5.mapper实现
- 6.Service接口
- 7.Service实现
- 8.修改自定义realm
- 9.启动测试
- 6.9 使用CacheManager
- 1.Cache 作用
- 2.使用shiro中默认EhCache实现缓存
- 1.引入依赖
- 2.开启缓存
- 3.启动刷新页面进行测试
- 3.shiro中使用Redis作为缓存实现
- 1.引入redis依赖
- 2.配置redis连接
- 3.启动redis服务
- 4.开发RedisCacheManager
- 5.开RedisCache实现
- 6.启动项目测试发现报错
- 7.再次启动测试,发现可以成功放入redis缓存
- 4. 加入验证码验证
- 0.开发页面加入验证码
6.整合SpringBoot项目实战
源码:https://download.csdn.net/download/qq_43753724/20031203?spm=1001.2014.3001.5501
6.0 整合思路
6.1 创建springboot项目
6.2 引入shiro依赖
复制代码
1
2
3
4
5
6<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.5.3</version> </dependency>
6.3 配置shiro环境
0.创建配置类
1.配置shiroFilterFactoryBean
复制代码
1
2
3
4
5
6
7
8
9
10@Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){ //创建shiro的filter ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //注入安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; }
2.配置WebSecurityManager
复制代码
1
2
3
4
5
6
7@Bean public DefaultWebSecurityManager getSecurityManager(Realm realm){ DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); defaultWebSecurityManager.setRealm(realm); return defaultWebSecurityManager; }
3.创建自定义realm
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class CustomerRealm extends AuthorizingRealm { //处理授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } //处理认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { return null; } }
4.配置自定义realm
复制代码
1
2
3
4
5
6//创建自定义realm @Bean public Realm getRealm(){ return new CustomerRealm(); }
5.编写控制器跳转至index.html
复制代码
1
2
3
4
5
6
7
8
9@Controller public class IndexController { @RequestMapping("index") public String index(){ System.out.println("跳转至主页"); return "index"; } }
6.启动springboot应用访问index
- 注意:
- 默认在配置好shiro环境后默认环境中没有对项目中任何资源进行权限控制,所有现在项目中所有资源都可以通过路径访问
7.加入权限控制
-
修改ShiroFilterFactoryBean配置
复制代码1
2
3
4
5
6
7//注入安全管理器 shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String,String> map = new LinkedHashMap<>(); map.put("/**","authc"); //配置认证和授权规则 shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-932Hxa4K-1625454917389)(Shiro 实战教程.assets/image-20200523102303320.png)]
- /** 代表拦截项目中一切资源 authc 代表shiro中的一个filter的别名,详细内容看文档的shirofilter列表
8.重启项目访问查看
6.4 常见过滤器
- 注意: shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置控制指定url的权限:
配置缩写 | 对应的过滤器 | 功能 |
---|---|---|
anon | AnonymousFilter | 指定url可以匿名访问 |
authc | FormAuthenticationFilter | 指定url需要form表单登录,默认会从请求中获取username 、password ,rememberMe 等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic | BasicHttpAuthenticationFilter | 指定url需要basic登录 |
logout | LogoutFilter | 登出过滤器,配置指定url就可以实现退出功能,非常方便 |
noSessionCreation | NoSessionCreationFilter | 禁止创建会话 |
perms | PermissionsAuthorizationFilter | 需要指定权限才能访问 |
port | PortFilter | 需要指定端口才能访问 |
rest | HttpMethodPermissionFilter | 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释 |
roles | RolesAuthorizationFilter | 需要指定角色才能访问 |
ssl | SslFilter | 需要https请求才能访问 |
user | UserFilter | 需要已登录或“记住我”的用户才能访问 |
6.5 认证实现
1. 在login.jsp中开发认证界面
复制代码
1
2
3
4
5
6<form action="${pageContext.request.contextPath}/user/login" method="post"> 用户名:<input type="text" name="username" > <br/> 密码 : <input type="text" name="password"> <br> <input type="submit" value="登录"> </form>
2. 开发controller
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27@Controller @RequestMapping("user") public class UserController { /** * 用来处理身份认证 * @param username * @param password * @return */ @RequestMapping("login") public String login(String username,String password){ //获取主体对象 Subject subject = SecurityUtils.getSubject(); try { subject.login(new UsernamePasswordToken(username,password)); return "redirect:/index.jsp"; } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("用户名错误!"); }catch (IncorrectCredentialsException e){ e.printStackTrace(); System.out.println("密码错误!"); } return "redirect:/login.jsp"; } }
- 在认证过程中使用subject.login进行认证
3.开发realm中返回静态数据(未连接数据库)
复制代码
1
2
3
4
5
6
7
8
9
10
11@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=========================="); String principal = (String) token.getPrincipal(); if("xiaochen".equals(principal)){ return new SimpleAuthenticationInfo(principal,"123",this.getName()); } return null; } }
4.启动项目以realm中定义静态数据进行认证
- 认证功能没有md5和随机盐的认证就实现啦
6.6 退出认证
1.开发页面退出连接
2.开发controller
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Controller @RequestMapping("user") public class UserController { /** * 退出登录 * */ @RequestMapping("logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout();//退出用户 return "redirect:/login.jsp"; } }
3.修改退出连接访问退出路径
4.退出之后访问受限资源立即返回认证界面
6.7 MD5、Salt的认证实现
1.开发数据库注册
0.开发注册界面
复制代码
1
2
3
4
5
6
7<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="立即注册"> </form>
1.创建数据表结构
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` int(6) NOT NULL AUTO_INCREMENT, `username` varchar(40) DEFAULT NULL, `password` varchar(40) DEFAULT NULL, `salt` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1;
2.项目引入依赖
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!--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> <!--druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.19</version> </dependency>
3.配置application.properties配置文件
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32server.port=8888 server.servlet.context-path=/shiro spring.application.name=shiro spring.mvc.view.suffix=/ spring.mvc.view.prefix=.jsp #数据源 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=123456 mybatis.type-aliases-package=com.baizhi.springboot_jsp_shiro.entity mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml #redis spring.redis.database=0 spring.redis.host=192.168.159.22 spring.redis.port=6379 #logging.level.root=debug logging.level.com.baizhi.springboot_jsp_shiro.dao=debug
4.创建entity
复制代码
1
2
3
4
5
6
7
8
9
10
11@Data @Accessors(chain = true) @AllArgsConstructor @NoArgsConstructor public class User { private String id; private String username; private String password; private String salt; }
5.创建DAO接口
复制代码
1
2
3
4
5@Mapper public interface UserDAO { void save(User user); }
6.开发mapper配置文件
复制代码
1
2
3
4<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id"> insert into t_user values(#{id},#{username},#{password},#{salt}) </insert>
7.开发service接口
复制代码
1
2
3
4
5public interface UserService { //注册用户方法 void register(User user); }
8.创建salt工具类
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class SaltUtils { /** * 生成salt的静态方法 * @param n * @return */ public static String getSalt(int n){ char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { char aChar = chars[new Random().nextInt(chars.length)]; sb.append(aChar); } return sb.toString(); } }
9.开发service实现类
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21@Service @Transactional public class UserServiceImpl implements UserService { @Autowired private UserDAO userDAO; @Override public void register(User user) { //处理业务调用dao //1.生成随机盐 String salt = SaltUtils.getSalt(8); //2.将随机盐保存到数据 user.setSalt(salt); //3.明文密码进行md5 + salt + hash散列 Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024); user.setPassword(md5Hash.toHex()); userDAO.save(user); } }
10.开发Controller
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22@Controller @RequestMapping("user") public class UserController { @Autowired private UserService userService; /** * 用户注册 */ @RequestMapping("register") public String register(User user) { try { userService.register(user); return "redirect:/login.jsp"; }catch (Exception e){ e.printStackTrace(); return "redirect:/register.jsp"; } } }
11.启动项目进行注册
2.开发数据库认证
0.开发DAO
复制代码
1
2
3
4
5
6
7
8@Mapper public interface UserDAO { void save(User user); //根据身份信息认证的方法 User findByUserName(String username); }
1.开发mapper配置文件
复制代码
1
2
3
4
5<select id="findByUserName" parameterType="String" resultType="User"> select id,username,password,salt from t_user where username = #{username} </select>
2.开发Service接口
复制代码
1
2
3
4
5
6
7public interface UserService { //注册用户方法 void register(User user); //根据用户名查询业务的方法 User findByUserName(String username); }
3.开发Service实现类
复制代码
1
2
3
4
5
6
7
8
9
10
11@Service("userService") @Transactional public class UserServiceImpl implements UserService { @Autowired private UserDAO userDAO; @Override public User findByUserName(String username) { return userDAO.findByUserName(username); } }
4.开发在工厂中获取bean对象的工具类
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@Component public class ApplicationContextUtils implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.context = applicationContext; } //根据bean名字获取工厂中指定bean 对象 public static Object getBean(String beanName){ return context.getBean(beanName); } }
5.修改自定义realm
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=========================="); //根据身份信息 String principal = (String) token.getPrincipal(); //在工厂中获取service对象 UserService userService = (UserService) ApplicationContextUtils.getBean("userService"); //根据身份信息查询 User user = userService.findByUserName(principal); if(!ObjectUtils.isEmpty(user)){ //返回数据库信息 return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName()); } return null; }
6.修改ShiroConfig中realm使用凭证匹配器以及hash散列
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14@Bean public Realm getRealm(){ CustomerRealm customerRealm = new CustomerRealm(); //设置hashed凭证匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //设置md5加密 credentialsMatcher.setHashAlgorithmName("md5"); //设置散列次数 credentialsMatcher.setHashIterations(1024); customerRealm.setCredentialsMatcher(credentialsMatcher); return customerRealm; }
6.8 授权实现
0.页面资源授权
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> <shiro:hasAnyRoles name="user,admin"> <li><a href="">用户管理</a> <ul> <shiro:hasPermission name="user:add:*"> <li><a href="">添加</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:delete:*"> <li><a href="">删除</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:update:*"> <li><a href="">修改</a></li> </shiro:hasPermission> <shiro:hasPermission name="user:find:*"> <li><a href="">查询</a></li> </shiro:hasPermission> </ul> </li> </shiro:hasAnyRoles> <shiro:hasRole name="admin"> <li><a href="">商品管理</a></li> <li><a href="">订单管理</a></li> <li><a href="">物流管理</a></li> </shiro:hasRole>
1.代码方式授权
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@RequestMapping("save") public String save(){ System.out.println("进入方法"); //获取主体对象 Subject subject = SecurityUtils.getSubject(); //代码方式 if (subject.hasRole("admin")) { System.out.println("保存订单!"); }else{ System.out.println("无权访问!"); } //基于权限字符串 //.... return "redirect:/index.jsp"; }
2.方法调用授权
- @RequiresRoles 用来基于角色进行授权
- @RequiresPermissions 用来基于权限进行授权
复制代码
1
2
3
4
5
6
7
8@RequiresRoles(value={"admin","user"})//用来判断角色 同时具有 admin user @RequiresPermissions("user:update:01") //用来判断权限字符串 @RequestMapping("save") public String save(){ System.out.println("进入方法"); return "redirect:/index.jsp"; }
3.授权数据持久化
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_pers -- ---------------------------- DROP TABLE IF EXISTS `t_pers`; CREATE TABLE `t_pers` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(80) DEFAULT NULL, `url` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` int(6) NOT NULL AUTO_INCREMENT, `name` varchar(60) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_role_perms -- ---------------------------- DROP TABLE IF EXISTS `t_role_perms`; CREATE TABLE `t_role_perms` ( `id` int(6) NOT NULL, `roleid` int(6) DEFAULT NULL, `permsid` int(6) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` int(6) NOT NULL AUTO_INCREMENT, `username` varchar(40) DEFAULT NULL, `password` varchar(40) DEFAULT NULL, `salt` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `id` int(6) NOT NULL, `userid` int(6) DEFAULT NULL, `roleid` int(6) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1;
4.创建dao方法
复制代码
1
2
3
4
5//根据用户名查询所有角色 User findRolesByUserName(String username); //根据角色id查询权限集合 List<Perms> findPermsByRoleId(String id);
5.mapper实现
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<resultMap id="userMap" type="User"> <id column="uid" property="id"/> <result column="username" property="username"/> <!--角色信息--> <collection property="roles" javaType="list" ofType="Role"> <id column="id" property="id"/> <result column="rname" property="name"/> </collection> </resultMap> <select id="findRolesByUserName" parameterType="String" resultMap="userMap"> SELECT u.id uid,u.username,r.id,r.NAME rname FROM t_user u LEFT JOIN t_user_role ur ON u.id=ur.userid LEFT JOIN t_role r ON ur.roleid=r.id WHERE u.username=#{username} </select> <select id="findPermsByRoleId" parameterType="String" resultType="Perms"> SELECT p.id,p.NAME,p.url,r.NAME FROM t_role r LEFT JOIN t_role_perms rp ON r.id=rp.roleid LEFT JOIN t_perms p ON rp.permsid=p.id WHERE r.id=#{id} </select>
6.Service接口
复制代码
1
2
3
4
5//根据用户名查询所有角色 User findRolesByUserName(String username); //根据角色id查询权限集合 List<Perms> findPermsByRoleId(String id);
7.Service实现
复制代码
1
2
3
4
5
6
7
8
9
10@Override public List<Perms> findPermsByRoleId(String id) { return userDAO.findPermsByRoleId(id); } @Override public User findRolesByUserName(String username) { return userDAO.findRolesByUserName(username); }
8.修改自定义realm
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class CustomerRealm extends AuthorizingRealm { @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //获取身份信息 String primaryPrincipal = (String) principals.getPrimaryPrincipal(); System.out.println("调用授权验证: "+primaryPrincipal); //根据主身份信息获取角色 和 权限信息 UserService userService = (UserService) ApplicationContextUtils.getBean("userService"); User user = userService.findRolesByUserName(primaryPrincipal); //授权角色信息 if(!CollectionUtils.isEmpty(user.getRoles())){ SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); user.getRoles().forEach(role->{ simpleAuthorizationInfo.addRole(role.getName()); //权限信息 List<Perms> perms = userService.findPermsByRoleId(role.getId()); if(!CollectionUtils.isEmpty(perms)){ perms.forEach(perm->{ simpleAuthorizationInfo.addStringPermission(perm.getName()); }); } }); return simpleAuthorizationInfo; } return null; } }
9.启动测试
6.9 使用CacheManager
1.Cache 作用
- Cache 缓存: 计算机内存中一段数据
- 作用: 用来减轻DB的访问压力,从而提高系统的查询效率
- 流程:
2.使用shiro中默认EhCache实现缓存
1.引入依赖
复制代码
1
2
3
4
5
6
7<!--引入shiro和ehcache--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.5.3</version> </dependency>
2.开启缓存
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//3.创建自定义realm @Bean public Realm getRealm(){ CustomerRealm customerRealm = new CustomerRealm(); //修改凭证校验匹配器 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //设置加密算法为md5 credentialsMatcher.setHashAlgorithmName("MD5"); //设置散列次数 credentialsMatcher.setHashIterations(1024); customerRealm.setCredentialsMatcher(credentialsMatcher); //开启缓存管理器 customerRealm.setCachingEnabled(true); customerRealm.setAuthorizationCachingEnabled(true); customerRealm.setAuthorizationCachingEnabled(true); customerRealm.setCacheManager(new EhCacheManager()); return customerRealm; }
3.启动刷新页面进行测试
- 注意:如果控制台没有任何sql展示说明缓存已经开启
3.shiro中使用Redis作为缓存实现
1.引入redis依赖
复制代码
1
2
3
4
5
6<!--redis整合springboot--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2.配置redis连接
这里我用虚拟机上面的redis
复制代码
1
2
3
4
5#redis spring.redis.database=0 spring.redis.host=192.168.159.22 spring.redis.port=6379
3.启动redis服务
复制代码
1
2
3
4
5➜ bin ls dump.rdb redis-check-aof redis-cli redis-server redis.conf redis-benchmark redis-check-rdb redis-sentinel redis-trib.rb ➜ bin ./redis-server redis.conf
4.开发RedisCacheManager
复制代码
1
2
3
4
5
6
7
8public class RedisCacheManager implements CacheManager { @Override public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException { System.out.println("缓存名称: "+cacheName); return new RedisCache<K,V>(cacheName); } }
5.开RedisCache实现
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71public class RedisCache<K,V> implements Cache<K,V> { private String cacheName; public RedisCache() { } public RedisCache(String cacheName) { this.cacheName = cacheName; } @Override public V get(K k) throws CacheException { System.out.println("获取缓存:"+ k); return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString()); } @Override public V put(K k, V v) throws CacheException { System.out.println("设置缓存key: "+k+" value:"+v); getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v); return null; } @Override public V remove(K k) throws CacheException { return (V) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString()); } @Override public v remove(k k) throws CacheException { return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString()); } @Override public void clear() throws CacheException { getRedisTemplate().delete(this.cacheName); } @Override public int size() { return getRedisTemplate().opsForHash().size(this.cacheName).intValue(); } @Override public Set<k> keys() { return getRedisTemplate().opsForHash().keys(this.cacheName); } @Override public Collection<v> values() { return getRedisTemplate().opsForHash().values(this.cacheName); } private RedisTemplate getRedisTemplate(){ RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } //封装获取redisTemplate private RedisTemplate getRedisTemplate(){ RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } }
6.启动项目测试发现报错
-
错误解释: 由于shiro中提供的simpleByteSource实现没有实现序列化,所有在认证时出现错误信息
-
解决方案: 需要自动salt实现序列化
-
自定义salt实现序列化
复制代码1
2
3
4
5
6
7//自定义salt实现 实现序列化接口 public class MyByteSource extends SimpleByteSource implements Serializable { public MyByteSource(String string) { super(string); } }
-
在realm中使用自定义salt
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("=========================="); //根据身份信息 String principal = (String) token.getPrincipal(); //在工厂中获取service对象 UserService userService = (UserService) ApplicationContextUtils.getBean("userService"); User user = userService.findByUserName(principal); if(!ObjectUtils.isEmpty(user)){ return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), new MyByteSource(user.getSalt()),this.getName()); } return null; }
-
7.再次启动测试,发现可以成功放入redis缓存
4. 加入验证码验证
0.开发页面加入验证码
-
开发控制器
复制代码1
2
3
4
5
6
7
8
9
10
11
12@RequestMapping("getImage") public void getImage(HttpSession session, HttpServletResponse response) throws IOException { //生成验证码 String code = VerifyCodeUtils.generateVerifyCode(4); //验证码放入session session.setAttribute("code",code); //验证码存入图片 ServletOutputStream os = response.getOutputStream(); response.setContentType("image/png"); VerifyCodeUtils.outputImage(220,60,os,code); }
-
放行验证码
- 开发页面
-
修改认证流程
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26@RequestMapping("login") public String login(String username, String password,String code,HttpSession session) { //比较验证码 String codes = (String) session.getAttribute("code"); try { if (codes.equalsIgnoreCase(code)){ //获取主体对象 Subject subject = SecurityUtils.getSubject(); subject.login(new UsernamePasswordToken(username, password)); return "redirect:/index.jsp"; }else{ throw new RuntimeException("验证码错误!"); } } catch (UnknownAccountException e) { e.printStackTrace(); System.out.println("用户名错误!"); } catch (IncorrectCredentialsException e) { e.printStackTrace(); System.out.println("密码错误!"); }catch (Exception e){ e.printStackTrace(); System.out.println(e.getMessage()); } return "redirect:/login.jsp"; }
-
修改salt不能序列化的问题
复制代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97//自定义salt实现 实现序列化接口 public class MyByteSource implements ByteSource,Serializable { private byte[] bytes; private String cachedHex; private String cachedBase64; //加入无参数构造方法实现序列化和反序列化 public MyByteSource(){ } public MyByteSource(byte[] bytes) { this.bytes = bytes; } public MyByteSource(char[] chars) { this.bytes = CodecSupport.toBytes(chars); } public MyByteSource(String string) { this.bytes = CodecSupport.toBytes(string); } public MyByteSource(ByteSource source) { this.bytes = source.getBytes(); } public MyByteSource(File file) { this.bytes = (new MyByteSource.BytesHelper()).getBytes(file); } public MyByteSource(InputStream stream) { this.bytes = (new MyByteSource.BytesHelper()).getBytes(stream); } public static boolean isCompatible(Object o) { return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream; } public byte[] getBytes() { return this.bytes; } public boolean isEmpty() { return this.bytes == null || this.bytes.length == 0; } public String toHex() { if (this.cachedHex == null) { this.cachedHex = Hex.encodeToString(this.getBytes()); } return this.cachedHex; } public String toBase64() { if (this.cachedBase64 == null) { this.cachedBase64 = Base64.encodeToString(this.getBytes()); } return this.cachedBase64; } public String toString() { return this.toBase64(); } public int hashCode() { return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0; } public boolean equals(Object o) { if (o == this) { return true; } else if (o instanceof ByteSource) { ByteSource bs = (ByteSource)o; return Arrays.equals(this.getBytes(), bs.getBytes()); } else { return false; } } private static final class BytesHelper extends CodecSupport { private BytesHelper() { } public byte[] getBytes(File file) { return this.toBytes(file); } public byte[] getBytes(InputStream stream) { return this.toBytes(stream); } } }
最后
以上就是顺利汉堡最近收集整理的关于Shiro整合SpringBoot项目实战的全部内容,更多相关Shiro整合SpringBoot项目实战内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复