Shiro简单使用:
首先导入依赖:
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.9.0</version> </dependency>-
-
-
-
然后创建.ini文件:
[users]
zhanfang=123,role1 //用户名为zhanfang,密码为123, 它的角色为role1
-
-
-
[roles]
role1=user:select,user:insert //该角色拥有的两个权限,其实就是两个字符串,这样写可读性较高,
-
-
-
-
-
然后创建Subject对象:
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:subject.ini"); //创建管理器工厂 SecurityManager securityManager = factory.getInstance(); //通过工厂拿到管理器对象securityManage SecurityUtils.setSecurityManager(securityManager); //对安全管理器对象进行全局设置 Subject subject = SecurityUtils.getSubject(); //获取用户主体//是先设置管理器对象,再获取subject主体的
-
-
-
-
然后拿到用户名和密码进行登录验证:
@RequestMapping("/subject") @ResponseBody public String subject(String username,String passward){ UsernamePasswordToken token = new UsernamePasswordToken(username, passward); //通过用户名和密码生成token字符串 try { subject.login(token); //调用subject中的login方法和ini中的数据进行比对,从而进行登录 boolean hasRole = subject.hasRole("role1"); //登录成功后产看是否有该角色 System.out.println(hasRole); subject.checkPermission("user:select"); // 查看该角色是否有该权限,无权限则抛出异常 UnauthorizedException subject.isPermitted("user:select") //同样是判断该角色是否有该权限,有则值为ture, 无权限则为false } catch (UnauthorizedException e) { return "无权限"; }catch (UnknownAccountException e){ return "用户不存在"; }catch (IncorrectCredentialsException e){ return "密码错误"; }catch (AuthenticationException e){ return "其他错误"; } return "登录成功"; }到此shiro的简单使用就介绍完毕
-
-
-
-
shiro的加密工具:
String aa="slei"
System.out.println(new Md5Hash(aa).toHex() ) //shiro提供了Md5Hash, 主要用来对字符串进行加密的, 通过toHex()就可以得到加密后的字符串
new Md5Hash(aa,"sedl") //拼接一个新的,再进行加密
new Md5Hash(aa,"sedl",3) //拼接三次,再进行加密
--------------------------
Springboot整合Shiro:
首先导入依赖:
<!-- 这个是springboot整合的所有的shiro相关的依赖,也包括上面的shiro-core依赖,所有shiro-core依赖可以不用导入了--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.9.0</version> </dependency>//在这个shiro-spring-boot-web-starter依赖中没有登录认证的类,所以需要你自己创建一个继承了AuthorizingRealm的bean对象,在这个bean中重写登录认证的方法,
否则就会报错"No bean of type 'org.apache.shiro.reaml.Realm' found "
//就是必须要创建一个MyRealm类,
-
-
-
-
-
-
-
然后在application.yml中进行设置:
shiro:
loginUrl: /aa/subject //访问需要登录认证的接口的时候,就会自动转跳到这个接口中,所以这个接口一般定义的是登录接口
-
-
-
-
然后创建MyRealm类:
这里面编写的是一些自定义的认证逻辑
@Component public class MyRealm extends AuthorizingRealm { @Resource private UserService userService; //重写的登录方法 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("开始运行"); //只有在安全管理器中导入了这个MyRealm类,在调用subject.login(token)的时候,才会调用这里面的方法 String name=authenticationToken.getPrincipal().toString(); //获取用户名 //在运行subject.login(token)的时候,就会自动调用这个方法 User user=userService.user(name); //通过用户名从数据库中查询用户信息 if(user!=null){ AuthenticationInfo info=new SimpleAuthenticationInfo( //运行认证逻辑 user, //这个参数就是一个唯一标识,在下面的权限授权方法中的principalCollection.getPrimaryPrincipal().toString() 获取的值就是这个参数值user.getPassword(), //数据库中的密码 ByteSource.Util.bytes("zh"), //加入"zh"进行解密, 当初就是加入"zh"进行加密 //在ShiroConfig中设置了会自动解密多少次,就是你自己创建的Shiro的配置类 //解密后和token中的密码进行比对,比对失败则抛出异常 //如果不需要解密,则可以直接不用这个参数,只使用三个参数就可以了 authenticationToken.getPrincipal().toString() //当前用户名称 ); return info; //拿到用户信息 } return null; } //重写的授权方法 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { principalCollection.getPrimaryPrincipal().toString() //这个是获取到登录认证中的第一个参数的值 return null; } }-
-
-
-
-
然后创建两个使用Shiro必备的配置类,安全管理器和授权对象:
@Configuration public class ShiroConfig { @Resource private MyRealm myRealm; //安全管理器,它其实就是下面的SecurityUtils默认的一个管理器对象 //所以在下面获取subject对象之前,就不用像上面那样通过SecurityUtils.setSecuritymanage()重新设置管理器对象了,这一点是比较方便的 @Bean public DefaultWebSecurityManager defaultWebSecurityManager(){ DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager(); HashedCredentialsMatcher matcher=new HashedCredentialsMatcher(); //创建解密对象 matcher.setHashAlgorithmName("md5"); //使用md5的解密方式 matcher.setHashIterations(3); //在登录认证的时候,设置MyRealm中的解密,会自动解密三次 myRealm.setCredentialsMatcher(matcher); //将解密对象导入到MyRealm中 defaultWebSecurityManager.setRealm(myRealm); //导入自定义的登录认证的类MyRealm return defaultWebSecurityManager; } //授权对象 @Bean public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition(){ DefaultShiroFilterChainDefinition dfc=new DefaultShiroFilterChainDefinition(); dfc.addPathDefinition("/aa/bb","anon"); //访问这个接口无需登录认证, anon表示无需登录认证 dfc.addPathDefinition("/aa/cc","authc"); //访问这个接口需要登录认证, anon表示需要登录认证 return dfc; } }-
-
-
-
-
-
最后编写认证代码:
@Controller @RequestMapping("/aa") public class AAController {@RequestMapping("/subject") @ResponseBody public String subject(String username,String passward){ UsernamePasswordToken token = new UsernamePasswordToken(username, passward); Subject subject= SecurityUtils.getSubject(); //这里是直接调用获取subject对象就行 //不需要像之前一样将安全管理器设置到SecurityUtils中了 try { subject.login(token); //它会自动调用MyRealm中的认证方法 boolean hasRole = subject.hasRole("role"); System.out.println(hasRole); subject.checkPermission("user:select"); } catch (UnauthorizedException e) { return "无权限"; }catch (UnknownAccountException e){ return "用户不存在"; }catch (IncorrectCredentialsException e){ return "密码错误"; }catch (AuthenticationException e){ return "其他错误"; } return "登录成功"; }}
总结:Springboot中使用Shiro的步骤:
第一:导入依赖
第二:设置application.yml
第三:创建MyRealm
第四:创建两个配置类,前者用来配置MyRealm类,后者用来设置拦截接口
第五:编写认证方法
注意:如果数据库中的密码是加密后的密码,并且加密了多次:
比如: new Md5Hash("zhan","zh",3) ,这里是加密后通过”zh"加密了三次
那么在配置类中的安全管理器中的给MyRealm中传入的解密对象,也要设置对应的解密次数
比如: matcher.setHashIterations(3); //解密三次
myRealm.setCredentialsMatcher(matcher); //将解密对象导入到MyRealm中
----------------------------
在安全管理器这个配置类中配置多个MyRealm对象:
就是ShiroConfig中必备的两个配置类中的其中一个配置类
//默认安全管理器对象 @Bean public DefaultWebSecurityManager defaultWebSecurityManager(){ 1,------------- //创建安全管理器对象 DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager(); 2,-------------- //设置认证策略 ModularRealmAuthenticator mra=new ModularRealmAuthenticator(); //创建认证对象 AtLeastOneSuccessfulStrategy als1=new AtLeastOneSuccessfulStrategy(); //创建认证策略,三个中,只要一个认证成功,就通过认证 //这个是默认的策略,其实可以不用设置 // mra.setAuthenticationStrategy(als); //给认证对象设置了这个认证策略 // // defaultWebSecurityManager.setAuthenticator(mra); //安全管理器中传入对应认证策略的对象 3,------------- //给指定MyRealm设置解密对象 HashedCredentialsMatcher matcher=new HashedCredentialsMatcher(); //创建解密对象 matcher.setHashAlgorithmName("md5"); //使用md5的解密方式 matcher.setHashIterations(1); //在登录认证的时候,设置MyRealm中的解密,会自动解密一次 myRealm.setCredentialsMatcher(matcher); //将解密对象导入到MyRealm中 4,------------ //将三个MyRealm认证对象放入集合中 List list=new ArrayList(); list.add(myRealm); list.add(myRealm2); list.add(myRealm3); defaultWebSecurityManager.setRealms(list); //通过setRealms可以导入多个MyRealm对象 return defaultWebSecurityManager; }
总结:
在Shiro中定义了三种认证策略
a) AtLeastOneSuccessfulStrategy: 只要有任意一个Realm验证成功,那么认证将视为成功, 一般都是使用这种策略,这种策略也是默认的策略
它将返回所有MyRealm身份认证成功的认证信息
比如:
这种策略它是三个MyRealm都会运行, 如果一个都没成功,则报"其他错误"
b) FirstSuccessfulStrategy: 只要有一个Realm验证成功,整体认证将是为成功,
它将返回第一个身份认证成功的认证信息,其他都将忽略
比如: 三个MyRealm都会运行,如果一个都没成功,则报"其他错误"
c) AllSuccessfulStrategy: 所有Realm成功,认证才视为成功
比如:
它会先运行第一个MyRealm, 成功后,才会运行第二个MyRealm
第二个MyRealm成功后,才会运行第三个MyRealm
只有三个MyRealm都认证成功后,才算是真正的认证成功
注意: 在上面的四步中 ,如果使用默认的策略,则第二步的设置认证策略可以不用写,这样就整体简便了很多
--------------------------
在shiro中设置RememberMe功能:
首先在shiro的两大配置类中进行配置:
在ShiroConfig中
@Configuration
public class ShiroConfig {
@Bean public DefaultWebSecurityManager defaultWebSecurityManager(){ //创建安全管理器对象 DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();//创建shiro专用的cookie对象 SimpleCookie cookie=new SimpleCookie("rememberMe"); cookie.setPath("/"); cookie.setMaxAge(100); //设置100秒 //创建Cookie管理器 CookieRememberMeManager cm=new CookieRememberMeManager(); cm.setCookie(cookie); //将专用cookie放入cookie管理器中 cm.setCipherKey("1234567898765432".getBytes()); //设置cookie的加密密钥,这样才能生效 //将cookie管理器放入安全管理器中 defaultWebSecurityManager.setRememberMeManager(cm); return defaultWebSecurityManager; }//shiro拦截器 @Bean public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition(){ DefaultShiroFilterChainDefinition dfc=new DefaultShiroFilterChainDefinition(); //设置拦截器 dfc.addPathDefinition("/aa/w","anon"); //访问这些接口不需要登录认证, anon表示无需认证 dfc.addPathDefinition("/aa/subject","anon"); dfc.addPathDefinition("/aa/w","authc"); //访问这些接口需要登录认证, anon表示需要认证 dfc.addPathDefinition("/logout","logout"); //退出接口 dfc.addPathDefinition("/aa/w","user"); //点击RememberMe功能后,只要登录一次,在后续的100秒就记住了你,不需要再次登录,就能进行访问 //只有点击RememberMe功能,该拦截器才会起作用 return dfc; } }拦截器的优先级:”user">"authc">"anon"
比如: 三个拦截器都拦截/aa/w接口
当访问/aa/w的时候,被拦截进行登录认证, 从这可以看出 "需登录认证(authc) " > "不需登录认证(anon) "
当在登录的时候,点击rememberMe的时候,你退出页面再次访问,会发现不需要重新进行登录认证,从这可以看出 "rememberMe功能的拦截器 (user) "> “需登录认证(authc) "
-
-
-
然后编写登录逻辑:
subject.login(username,password, rememberMe) //这里是前端传入用户名,密码,以及布尔值,当rememberMe为true的时候,就表示开启rememberMe功能
//在开启rememberMe功能并登录成功后,浏览器会得到一个名称为“rememberMe” 的 cookie对象
-
-
-
登录时开启RememberMe功能时报错:
当你开启RememberMe功能的时候,会出现报错"SerializationException", 这是因为Shiro框架在执行"RememberMe"功能时,会将登录认证中查询到的数据库中的用户信息User进行序列化,如果User这个实体类没有继承序列化接口"Serializable",那么就会报错
解决方法就是: public class User implements Serializable{ //这个User实体类是用来接受从数据库中查询的用户的信息的
}
---------------------
在shiro中实现退出功能:
一,使用shiro拦截器进行退出操作
@Bean
public DefaultShiroFilterChainDefinition defaultShiroFilterChainDefinition(){
DefaultShiroFilterChainDefinition dfc=new DefaultShiroFilterChainDefinition(); //设置拦截器
dfc.addPathDefinition("/logout","logout"); //退出接口,/logout这个接口是不存在的,理论上拦截到/logout接口的时候,就会自动执行拦截器中的退出操作
//但是不知道为什么使用这种方法是无法正常退出的,总是失败报404,所以还是不要用了
}
-
-
-
-
-
-
二,自定义Controller进行退出操作
@RequestMapping("/logout") @ResponseBody public String logout(){ Subject subject = SecurityUtils.getSubject(); //获取subject主体 subject.logout(); //进行退出 return "退出成功"; }
-----------------
授权和角色认证:
首先在shiro框架中设置权限和角色:
在MyRealm中:
@Component public class MyRealm extends AuthorizingRealm { @Resource private UserService userService; //重写的登录方法 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {}
//重写的授权方法 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { String name=principalCollection.getPrimaryPrincipal().toString(); //获取用户名称 List<String > role=userService.getRoleInfo(name); //从数据库中查询用户的所有角色 List<String> permiseion = null; if(role.size()>0){ //判断是否有角色,权限是来自与角色的,没有角色就不用查权限了 permiseion=userService.getPermisionInfo(role); //获取给用户所有权限 } SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); info.addRoles(role); //添加多个角色 if(permiseion!=null){ //判断是否有权限 info.addStringPermissions(permiseion); //添加多个权限 }info.addRole("aa"); //添加一个角色 info.addStringPermiseion("user:add") //添加一个权限 return info; //这是最重要的, 每当进行权限和角色判断,都是在这个info对象中进行比对的} }-
-
-
-
-
然后使用注解给接口设置访问权限:
@RestController @RequestMapping("/roles") public class RolesController { @RequiresRoles("admin") //角色验证 @RequiresPermissions("user:select") //权限认证 @RequestMapping("/aa") public String roles(){ return "验证角色和权限成功成功"; } }-
-
-
-
然后设置异常处理器处理异常:
@RestControllerAdvice public class ProjectAdvice { @ExceptionHandler(Exception.class) public String Exception(Exception e){ return "出现异常"; }@ExceptionHandler(UnauthorizedException.class) public String aa(UnauthorizedException e){ return e.getMessage(); //当没有权限和没有角色都是抛出这个异常的 //没角色时打印结果为:Subject does not have role [admin] //没权限时打印结果为:Subject does not have permission [user:select] }}
---------------------
shiro权限页面
首先导入依赖:
<dependency> //这是thymeleaf整合shiro的一个依赖 <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency>-
-
-
然后编写配置
@Configuration public class ShiroConfig {@Bean public ShiroDialect shiroDialect(){ return new ShiroDialect(); //主要用来解析thymeleaf中的shiro:相关的属性 } }-
-
-
-
然后在动态页面shiro.html中: 注意:一定要在templates中的页面中才可以使用shiro, 因为只有这里面的页面才会被thymeleaf引擎进行加载
<body>
//是否拥有指定角色
<h1 shiro:lacksrole="admin">没角色admin才会显示</h1> //这是第一种写法,直接将shiro:hasRole当成属性来用 <h1 shiro:hasrole="admin">有角色admin才会显示</h1> <shiro:hasRole name="admin">有角色admin才会显示 </shiro:hasRole> //这是第二种写法,直接将shiro:hasRole当成标签类使用, 是不区分大小写的,shiro:hasrole也可以
// 是否登录
<shiro:guest>游客</shiro:guest> //未执行subject.login()的时候才会显示 <shiro:user>成功登录</shiro:user> //执行了subject.login()才会显示//是否拥有指定权限
<shiro:lacksPermission name="user:add">没有权限user:add才会显示</shiro:lacksPermission> <shiro:hasPermission name="user:add">拥有权限user:add才会显示</shiro:hasPermission>
</body>