文章目录
- 一、 springboot实现普通登录
- 1 添加依赖
- 2 编写配置文件
- 3 新建实体类和mapper
- 4 编写业务层代码
- 5 编写控制器
- 6 编写启动类
- 7 编写登录页面和主页面
- 二、 springboot整合shiro实现登录认证和凭证匹配
- 1 添加依赖
- 2 自定义Realm
- 3 编写配置
- 4 userService新增单元方法:使用shiro认证
- 5 凭证匹配器
- 5.1 修改ShiroConfig
- 5.2 修改MyRealm
- 三、 remember me实现---shiro已经集成
- 1 修改application.yml-----增加shiro的loginUrl地址
- 2 增加记住我的按钮
- 3 修改Controller
- 4 修改ShiroConfig
- 四、 配置退出
- 1 修改配置类
- 2 修改主页面
- 五、 授权
- 1 简介
- 2 Thymeleaf整合shiro
- 2.1 添加依赖
- 2.2 修改配置类(负责解析thymeleaf中shiro的相关属性)
- 2.3 修改Realm
- 2.4 修改main.html
- 3 使用注解判断方法是否具有权限执行
- 3.1 修改main.html
- 3.2 修改UserController类
- 3.3 新建依赖处理类
- 六 EHCache
- 1 ehcache简介
- 2 EHCache API演示
- 2.1 添加依赖
- 2.2 新建配置文件
- 七 Shiro和EhCache整合
- 1 添加依赖
- 2 编写ehcache缓存配置
- 3 修改配置文件
- 八 Shiro中Session对象获取
一、 springboot实现普通登录
1 添加依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.bjsxt</groupId><artifactId>02_shiro_springboot_login</artifactId><version>1.0-SNAPSHOT</version><!--配置继承--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.10.RELEASE</version></parent><!--配置依赖--><dependencies><!--配置web启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--配置mybatis启动器--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.1</version></dependency><!--配置mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.48</version></dependency><!--配置Thrmeleaf启动器--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency></project>
2 编写配置文件
新建application.yml
spring:datasource:driver-class-name: com.mysql.jdbc.Driverusername: rooturl: jdbc:mysql://localhost:3306/springboot-loginpassword: 1234
##配置mapper的xml文件的路径
mybatis:mapper-locations: classpath:mybatis/*.xml
3 新建实体类和mapper
新建com.bjsxt.pojo.User
public class User {private Long id;private String username;private String pwd;// 省略getter和setter // 省略构造方法 }
新建com.bjsxt.mapper.UserMapper
package com.bjsxt.mapper;public interface UserMapper {//根据用户名查询用户信息@Select("select * from t_user where uname=#{uname}")User selUserInfoMapper(@Param("uname") String uname); }
4 编写业务层代码
新建com.bjsxt.service.UserService及实现类
public interface UserService {//用户登录User selUserInfoService(String uname);
}
@Service
public class UserServiceImpl implements UserService {//声明mapper属性@Autowiredprivate UserMapper userMapper;//用户登录@Overridepublic User selUserInfoService(String uname) {return userMapper.selUserInfoMapper(uname);}
}
5 编写控制器
新建com.bjsxt.controller.UserController
package com.bjsxt.controller;@Controller
public class UserController {//声明service属性@Autowiredprivate UserService userService;//声明单元方法:登录认证@RequestMapping("userLogin")public String userLogin(String uname,String pwd){//1.根据用户名获取用户信息User user=userService.selUserInfoService(uname);//2.判断用户名是否合法if(user!=null){//3.校验密码if(user.getPwd().equals(pwd)){//认证成功return "main";}}return "error";}//声明公共单元方法完成页面的内部转发@RequestMapping("{uri}")public String getPage(@PathVariable String uri){return uri;}}
6 编写启动类
新建com.bjsxt.ShiroApplication
package com.bjsxt;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.bjsxt.mapper")
public class ShiroApplication {public static void main(String[] args) {SpringApplication.run(ShiroApplication.class,args);}}
7 编写登录页面和主页面
新建templates/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h3>SpringBoot整合Shiro登录案例</h3><hr><!--创建登录页面--><form action="userLogin" method="post">用户名: <input type="text" name="uname" value=""><br>密码: <input type="password" name="pwd" value=""><br><input type="submit" value="登录"></form></body>
</html>
新建templates/main.html。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>我是主页面
</body>
</html>
二、 springboot整合shiro实现登录认证和凭证匹配
1 添加依赖
<dependencies><!-- 注释掉web启动器是因为shiro-spring-boot-web-starter依赖了spring-boot-starter-web --><!-- <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>--><!--配置shiro的启动器--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.4.2</version></dependency></dependencies>
2 自定义Realm
新建com.bjsxt.shiro.MyRealm编写认证逻辑
package com.bjsxt.shiro;//配置为Bean对象
@Component
public class MyRealm extends AuthorizingRealm {//声明业务层属性@Autowiredprivate UserService userService;//自定义授权策略@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}//自定义认证策略@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//声明认证代码//1.获取用户传递的用户名信息Object principal = token.getPrincipal();//2.根据用户名获取数据库中的用户信息User user = userService.selUserInfoService((String) principal);//3.认证if(user!=null){//用户名是正确的//4.认证密码AuthenticationInfo info= new SimpleAuthenticationInfo(principal,user.getPwd(), user.getUname());return info;}return null;}
}
3 编写配置
新建com.bjsxt.config.ShiroConfig,编写配置
package com.bjsxt.config;@Configuration
public class ShiroConfig {//声明MyRealm属性@Autowiredprivate MyRealm myRealm;//声明bean方法@Beanpublic DefaultWebSecurityManager securityManager(){DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();defaultWebSecurityManager.setRealm(myRealm);return defaultWebSecurityManager;}
4 userService新增单元方法:使用shiro认证
package com.bjsxt.controller;@Controller
public class UserController {//声明单元方法:使用shiro认证@RequestMapping("userLogin2")public String userLogin2(String uname,String pwd){//1.获取subject对象Subject subject = SecurityUtils.getSubject();//2.认证//创建认证对象存储认证信息AuthenticationToken token= new UsernamePasswordToken(uname,pwd);try{subject.login(token);return "redirect:main";}catch(Exception e){e.printStackTrace();}return "redirect:login";}//声明公共单元方法完成页面的内部转发@RequestMapping("{uri}")public String getPage(@PathVariable String uri){return uri;}}
此时登录出现问题:访问login时报错,错误说找不到login.jsp,应该找的是login.html
解决:在ShiroConfig配置类自定义shiro过滤器参数bean
package com.bjsxt.config;@Configuration
public class ShiroConfig {//自定义shiro过滤器参数bean@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();definition.addPathDefinition("/login", "anon");definition.addPathDefinition("/userLogin", "anon");definition.addPathDefinition("/main", "anon");definition.addPathDefinition("/**", "user");return definition;}}
将
<form action="userLogin" method="post">
改为<form action="userLogin2" method="post">
<body><h3>SpringBoot整合Shiro登录案例</h3><hr><!--创建登录页面--><form action="userLogin2" method="post">用户名: <input type="text" name="uname" value=""><br>密码: <input type="password" name="pwd" value=""><br><input type="submit" value="登录"></form>
</body>
启动测试
5 凭证匹配器
首先将数据库中用户张三的密码改为:6bdae6366c1e46d541eb0ca9547d974c
5.1 修改ShiroConfig
@Bean
public DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager defaultWebSecurityManager= new DefaultWebSecurityManager();//创建凭证匹配器HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();matcher.setHashAlgorithmName("md5");matcher.setHashIterations(2);myRealm.setCredentialsMatcher(matcher);//将自定义的认证策略对象注入到SecurityManagerdefaultWebSecurityManager.setRealm(myRealm);return defaultWebSecurityManager;
}
5.2 修改MyRealm
修改MyRealm中doGetAuthenticationInfo方法。
//自定义认证策略@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//声明认证代码//1.获取用户传递的用户名信息Object principal = token.getPrincipal();//2.根据用户名获取数据库中的用户信息User user = userService.selUserInfoService((String) principal);//3.认证if(user!=null){//用户名是正确的//4.认证密码AuthenticationInfo info= new SimpleAuthenticationInfo(principal,user.getPwd(), ByteSource.Util.bytes(user.getUid()+""),user.getUname());return info;}return null;}
三、 remember me实现—shiro已经集成
1 修改application.yml-----增加shiro的loginUrl地址
spring:datasource:driver-class-name: com.mysql.jdbc.Driverusername: rooturl: jdbc:mysql://localhost:3306/springboot-loginpassword: 1234
##配置mapper的xml文件的路径
mybatis:mapper-locations: classpath:mybatis/*.xml
shiro:##当用户访问某个需要登录的功能时,但是被shiro内置的过滤器拦截后,判断本次请求##没有登录,而是直接访问的,则重定向到loginUrl的路径资源响应给用户loginUrl: /login
2 增加记住我的按钮
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><h3>SpringBoot整合Shiro登录案例</h3><hr><!--创建登录页面--><form action="userLogin2" method="post">用户名: <input type="text" name="uname" value=""><br>密码: <input type="password" name="pwd" value=""><br><input type="submit" value="登录"> <input type="checkbox" name="rememberme" value="true">记住俺</form></body>
</html>
3 修改Controller
@RequestMapping("userLogin2")public String userLogin2(String uname,String pwd,@RequestParam(defaultValue = "false") Boolean rememberme){//1.获取subject对象Subject subject = SecurityUtils.getSubject();//2.认证//创建认证对象存储认证信息AuthenticationToken token= new UsernamePasswordToken(uname,pwd,rememberme);try{subject.login(token);return "redirect:main";}catch(Exception e){e.printStackTrace();}return "redirect:login";}
4 修改ShiroConfig
@Beanpublic DefaultWebSecurityManager securityManager(){DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();//创建凭证匹配器HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();//设置匹配器的加密算法matcher.setHashAlgorithmName("md5");//设置匹配器的迭代加密次数matcher.setHashIterations(2);//将匹配器注入到自定义的认证策略对象中myRealm.setCredentialsMatcher(matcher);//将自定义的认证策略对象注入到SecurityManagerdefaultWebSecurityManager.setRealm(myRealm);//将CookieRememberMeManager对象注入到SecurityManager,开启了rememberme功能defaultWebSecurityManager.setCacheManager(ehCacheManager());return defaultWebSecurityManager;}//设置Cookie的信息public SimpleCookie rememberMeCookie(){SimpleCookie simpleCookie=new SimpleCookie("rememberMe");//设置有效路径simpleCookie.setPath("/");//设置声明周期simpleCookie.setMaxAge(30*24*60*60);//返回设置的cookiereturn simpleCookie;}//创建rememberMeManager对象public CookieRememberMeManager rememberMeManager(){//创建CookieRememberMeManager对象CookieRememberMeManager cookieRememberMeManager=new CookieRememberMeManager();//注入Cookie对象cookieRememberMeManager.setCookie(rememberMeCookie());//设置密钥cookieRememberMeManager.setCipherKey(Base64.decode("MTIzNDU2Nzg="));//返回return cookieRememberMeManager;}//自定义shiro过滤器参数bean----`definition.addPathDefinition("/**", "user");`@Beanpublic ShiroFilterChainDefinition shiroFilterChainDefinition(){DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();definition.addPathDefinition("/login", "anon");definition.addPathDefinition("/userLogin2", "anon");//开启shiro内置的退出过滤器,完成退出功能definition.addPathDefinition("/logout", "logout");//definition.addPathDefinition("/main", "anon");definition.addPathDefinition("/**", "user");return definition;}
四、 配置退出
1 修改配置类
修改ShiroConfig类,添加logout filter 对应的url。
definition.addPathDefinition("/logout", "logout");
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();definition.addPathDefinition("/doLogin", "anon");definition.addPathDefinition("/logout", "logout");definition.addPathDefinition("/**", "authc");return definition;
}
2 修改主页面
在index.html页面中添加超链接。跳转到/logout后会由shiro内置filter进行拦截。
<body>
index.html
<a href="/logout">退出</a>
</body>
五、 授权
1 简介
授权就是判断认证用户是否具有指定角色或指定权限。
Shiro可以和JSP整合也可以和Thymeleaf整合,我们讲解SpringBoot的视图技术Thymeleaf整合Shiro。
只要是授权就执行Realm的doGetAuthorizationInfo进行判断,而触发doGetAuthorizationInfo的方式,常用的就两种:
(1)在页面中通过shiro:xxxx 属性进行判断
(2)在java代码中通过注解@RequiresXXX
thymeleaf中常用属性
需要在html页面中添加属性
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
1.1 shiro:user=””
认证通过或已记住的用户。
1.2 shiro:authenticated=””
认证通过的用户。不包含记住的用户。
1.3 shiro:principal
输出认证用户信息。shiro:principal/
1.4 shiro:hasRole=“admin”
判断是否具有指定角色。
1.5 shiro:lacksRole=“admin”
判断是否不是没有指定角色。
1.6 shiro:hasAllRoles=“role1,role2”
判断指定角色用户是否都具有。
1.7 shiro:hasAnyRoles=“role1,role2”
只要用户具有其中一个角色就表示判断通过。
1.8 shiro:hasPermission=“userInfo:add”
是否具有指定权限。
1.9 shiro:lacksPermission=“userInfo:del”
是否不具有指定权限
1.10 shiro:hasAllPermissions=“userInfo:view, userInfo:add”
是否全具有指定权限。
1.11 shiro:hasAnyPermissions=“userInfo:view, userInfo:del”
只要有其中任何一个权限即可。
2 Thymeleaf整合shiro
2.1 添加依赖
<dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version>
</dependency>
2.2 修改配置类(负责解析thymeleaf中shiro的相关属性)
//创建解析Thymeleaf中的shiro属性的对象,由SpringBoot项目底层自动调用@Beanpublic ShiroDialect shiroDialect() {return new ShiroDialect();}
2.3 修改Realm
绑定用户具有的角色和权限,相关数据应该是从数据库中取出。
//自定义授权策略@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//1.从数据库中获取用户的权限信息//2.将权限信息存储到shiro授权对象中System.out.println("我是授权认证方法,我被执行了");SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();info.addRole("role1");info.addRole("role2");info.addStringPermission("user:insert");info.addStringPermission("user:update");info.addStringPermission("sys:*");return info;}
2.4 修改main.html
访问页面后会发现“有角色”不显示,“有权限”显示。
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>我是主页面<a href="/logout">退出</a><hr><span shiro:hasRole="role3">有角色</span><br><span shiro:user="">shiro:user=””认证通过或已记住的用户</span><br><span shiro:authenticated="">shiro:authenticated=””认证通过的用户。不包含记住的用户。</span><br><hr><a href="/demo">测试后台逻辑代码的授权</a></body>
</html>
3 使用注解判断方法是否具有权限执行
方法:可以用控制器方法,也可以是业务方法。常在控制器方法上添加注解进行判断。
常用注解:
(1)@RequiresPermissions("") 必须具有指定权限
(2)@RequiresAuthentication 必须已经认证
(3)@RequiresRoles("") 必须具有指定角色
(4)@RequiresUser 必须是已认证或记住用户
(5)@RequiresGuest 必须是访客
3.1 修改main.html
添加下面内容
<a href="/isPermission">跳转到判断权限的控制器</a>
3.2 修改UserController类
@RequestMapping("/isPermission")
@ResponseBody
@RequiresPermissions("abc:jqk")
public String isPermission(){return "OK";
}
当用户登录成后,点击超链接“跳转到判断权限的”超链接会出现500错误页面信息。
3.3 新建依赖处理类
新建com.bjsxt.controller.NoPermissionException
@ControllerAdvice
public class NoPermissionException {@ResponseBody@ExceptionHandler(UnauthorizedException.class)public String handleShiroException(Exception ex) {return "无权限";}@ResponseBody@ExceptionHandler(AuthorizationException.class)public String AuthorizationException(Exception ex) {return "权限认证失败";}
}
六 EHCache
1 ehcache简介
EHCache是sourceforge的开源缓存项目,现已经具有独立官网,网址:(http://www.ehcache.org)。其本身是纯JAVA实现的,所以可以和绝大多数Java项目无缝整合,例如:Hibernate的缓存就是基于EHCache实现的。
EHCache支持内存和磁盘缓存,默认是存储在内存中的,当内存不够时允许把缓存数据同步到磁盘中,所以不需要担心内存不够问题。
EHCache支持基于Filter的Cache实现,同时也支持Gzip压缩算法提高响应速度。
2 EHCache API演示
2.1 添加依赖
从3.0版本开始groupid为org.ehcache
<dependencies><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.6.11</version></dependency>
</dependencies>
2.2 新建配置文件
在src/main/resources中新建ehcache.xml。
属性含义:
maxElementsInMemory:缓存中允许创建的最大对象数。
eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。
timeToIdleSeconds:缓存数据的钝化时间,取值0表示无限长。
timeToLiveSeconds:缓存数据的生存时间,取值0表示无限长。
overflowToDisk:内存不足时,是否启用磁盘缓存。
memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache><!-- 磁盘缓存位置 --><diskStore path="java.io.tmpdir/ehcache"/><!-- 默认缓存 --><defaultCachemaxEntriesLocalHeap="10000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"maxEntriesLocalDisk="10000000"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"><persistence strategy="localTempSwap"/></defaultCache><!-- helloworld缓存 --><cache name="HelloWorldCache"maxElementsInMemory="1000"eternal="false"timeToIdleSeconds="5"timeToLiveSeconds="5"overflowToDisk="false"memoryStoreEvictionPolicy="LRU"/>
</ehcache>
七 Shiro和EhCache整合
Shiro支持很多第三方缓存工具。官方提供了shiro-ehcache,实现了把EHCache当做Shiro的缓存工具的解决方案。其中最好用的一个功能是就是缓存认证执行的Realm方法,减少对数据库的访问。
1 添加依赖
添加shiro-ehcache依赖。
commons-io主要是为了使用里面的工具类。本质和当前整合功能没有关系。
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.2</version>
</dependency>
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version>
</dependency>
2 编写ehcache缓存配置
在resources下新建ehcache/ehcache-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="ehcache" updateCheck="false"><!-- 磁盘缓存位置 -->
<diskStore path="java.io.tmpdir"/>
<!-- 默认缓存 -->
<defaultCachemaxEntriesLocalHeap="1000"eternal="false"timeToIdleSeconds="3600"timeToLiveSeconds="3600"overflowToDisk="false">
</defaultCache><!-- 登录记录缓存 锁定10分钟 -->
<cache name="loginRecordCache"maxEntriesLocalHeap="2000"eternal="false"timeToIdleSeconds="600"timeToLiveSeconds="0"overflowToDisk="false"statistics="true">
</cache></ehcache>
3 修改配置文件
@Bean
public DefaultWebSecurityManager securityManager() {DefaultWebSecurityManager manager = new DefaultWebSecurityManager();HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();hashedCredentialsMatcher.setHashAlgorithmName("md5");hashedCredentialsMatcher.setHashIterations(2);myRealm.setCredentialsMatcher(hashedCredentialsMatcher);manager.setRealm(myRealm);manager.setRememberMeManager(rememberMeManager());manager.setCacheManager(getEhCacheManager());return manager;
}@Bean
public EhCacheManager ehCacheManager(){EhCacheManager ehCacheManager = new EhCacheManager();InputStream is = null;try {is = ResourceUtils.getInputStreamForPath("classpath:ehcache/ehcache-shiro.xml");} catch (IOException e) {e.printStackTrace();}net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(is);ehCacheManager.setCacheManager(cacheManager);return ehCacheManager;
}
八 Shiro中Session对象获取
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("key","value");