一、总结与计划
1.1 总结
-
SpringBoot整合Shiro完成权限管理
-
Shiro功能
-
-
认证
-
授权
-
-
SpringBoot项目部署
-
jar包
-
nohup java -jar *.jar
-
-
layui使用
1.2 计划
-
shiro
-
加密(认证)
-
授权
-
过滤器
-
注解
-
java代码
-
HTML(√)
-
-
缓存管理
-
session管理
-
RememberMe
-
多Realm的配置
-
二、加密
明文-----(加密规则)-----密文
加密规则可以自定义,在项目开发中我们通常使用BASE64和MD5编码方式
BASE64:可反编码的编码方式(对称)
明文----密文
密文----明文
MD5: 不可逆的编码方式(非对称)
明文----密文
-
如果数据库用户的密码存储的密文,Shiro该如何完成验证呢?
-
使用Shiro提供的加密功能,对输入的密码进行加密之后再进行认证。
2.1 加密介绍
2.2 Shiro使用加密认证
-
配置matcher
@Configuration public class ShiroConfig { //... @Bean public HashedCredentialsMatcher getHashedCredentialsMatcher(){ HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //matcher就是用来指定加密规则 //加密方式 matcher.setHashAlgorithmName("md5"); //hash次数 matcher.setHashIterations(1); //此处的循环次数要与用户注册是密码加密次数一致 return matcher; } //自定义Realm @Bean public MyRealm getMyRealm( HashedCredentialsMatcher matcher ){ MyRealm myRealm = new MyRealm(); myRealm.setCredentialsMatcher(matcher); return myRealm; } //... }
2.3 用户注册密码加密处理
-
registh.html
<form action="/user/regist" method="post"> <p>帐号:<input type="text" name="userName"/></p> <p>密码:<input type="text" name="userPwd"/></p> <p><input type="submit" value="提交注册"/></p> </form>
-
UserController
@Controller @RequestMapping("user") public class UserController { @Resource private UserServiceImpl userService; @RequestMapping("/regist") public String regist(String userName,String userPwd) { System.out.println("------注册"); //注册的时候要对密码进行加密存储 Md5Hash md5Hash = new Md5Hash(userPwd); System.out.println("--->>>"+ md5Hash.toHex()); //加盐加密 int num = new Random().nextInt(90000)+10000; //10000—99999 String salt = num+""; Md5Hash md5Hash2 = new Md5Hash(userPwd,salt); System.out.println("--->>>"+md5Hash2); //加盐加密+多次hash Md5Hash md5Hash3 = new Md5Hash(userPwd,salt,3); System.out.println("--->>>"+md5Hash3); //SimpleHash hash = new SimpleHash("md5",userPwd,num,3); //将用户信息保存到数据库时,保存加密后的密码,如果生成的随机盐,盐也要保存 return "login"; } }
2.4 如果密码进行了加盐处理,则Realm在返回认证数据时需要返回盐
-
在自定义Realm中:
public class MyRealm extends AuthorizingRealm { @Resource private UserDAO userDAO; @Resource private RoleDAO roleDAO; @Resource private PermissionDAO permissionDAO; public String getName() { return "myRealm"; } /** * 获取认证的安全数据(从数据库查询的用户的正确数据) */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //参数authenticationToken就是传递的 subject.login(token) // 从token中获取用户名 UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; String username = token.getUsername(); //根据用户名,从数据库查询当前用户的安全数据 User user = userDAO.queryUserByUsername(username); // AuthenticationInfo info = new SimpleAuthenticationInfo( // username, //当前用户用户名 // user.getUserPwd(), //从数据库查询出来的安全密码 // getName()); //如果数据库中用户的密码是加了盐的 AuthenticationInfo info = new SimpleAuthenticationInfo( username, //当前用户用户名 user.getUserPwd(), //从数据库查询出来的安全密码 ByteSource.Util.bytes(user.getPwdSalt()), getName()); return info; } }
三、退出登录
-
在Shiro过滤器中进行配置,配置logut对应的路径
filterMap.put("/exit","logout");
-
在页面的“退出”按钮上,跳转到logout对应的url
<a href="exit">退出</a>
四、授权
用户登录成功之后,要进行响应的操作就需要有对应的权限;在进行操作之前对权限进行检查—授权
权限控制通常有两类做法:
不同身份的用户登录,我们现在不同的操作菜单(没有权限的菜单不现实)
对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足
4.1 HTML授权
-
在菜单页面只显示当前用户拥有权限操作的菜单
-
shiro标签
<shiro:hasPermission name="sys:c:save"> <dd><a href="javascript:;">入库</a></dd> </shiro:hasPermission>
4.2 过滤器授权
-
在shiro过滤器中对请求的url进行权限设置
filterMap.put("/c_add.html","perms[sys:c:save]"); //设置未授权访问的页面路径—当权限不足时显示此页面 filter.setUnauthorizedUrl("/lesspermission.html");
4.3 注解授权
-
配置Spring对Shiro注解的支持:ShiroConfig.java
@Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator(); autoProxyCreator.setProxyTargetClass(true); return autoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager securityManager){ AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; }
-
在请求的控制器添加权限注解
@Controller @RequestMapping("customer") public class CustomerController { @RequestMapping("list") //如果没有 sys:k:find 权限,则不允许执行此方法 @RequiresPermissions("sys:k:find") // @RequiresRoles("") public String list(){ System.out.println("----------->查询客户信息"); return "customer_list"; } }
-
通过全局异常处理,指定权限不足时的页面跳转
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler public String doException(Exception e){ if(e instanceof AuthorizationException){ return "lesspermission"; } return null; } }
4.4 手动授权
-
在代码中进行手动的权限校验
Subject subject = SecurityUtils.getSubject(); if(subject.isPermitted("sys:k:find")){ System.out.println("----------->查询客户信息"); return "customer_list"; }else{ return "lesspermission"; }
五、缓存使用
使用Shiro进行权限管理过程中,每次授权都会访问realm中的doGetAuthorizationInfo方法查询当前用户的角色及权限信息,如果系统的用户量比较大则会对数据库造成比较大的压力
Shiro支持缓存以降低对数据库的访问压力(缓存的是授权信息)
5.1 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
5.2 配置缓存策略
-
在resources目录下创建一个xml文件(ehcache.xml)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
<diskStore path="C:\TEMP" />
<cache name="users" timeToLiveSeconds="300" maxEntriesLocalHeap="1000"/>
<defaultCache name="defaultCache"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="100000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
<!--缓存淘汰策略:当缓存空间比较紧张时,我们要存储新的数据进来,就必然要删除一些老的数据
LRU 最近最少使用
FIFO 先进先出
LFU 最少使用
-->
</ehcache>
5.3 加入缓存管理
-
ShiroConfig.java
@Bean
public EhCacheManager getEhCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
return ehCacheManager;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
securityManager.setCacheManager(getEhCacheManager());
return securityManager;
}
六、session管理
Shiro进行认证和授权是基于session实现的,Shiro包含了对session的管理
-
如果我们需要对session进行管理
-
自定义session管理器
-
将自定义的session管理器设置给SecurityManager
-
-
配置自定义SessionManager:ShiroConfig.java
@Bean public DefaultWebSessionManager getDefaultWebSessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); System.out.println("----------"+sessionManager.getGlobalSessionTimeout()); // 1800000 //配置sessionManager sessionManager.setGlobalSessionTimeout(5*60*1000); return sessionManager; } @Bean public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm); securityManager.setCacheManager(getEhCacheManager()); securityManager.setSessionManager(getDefaultWebSessionManager()); return securityManager; }