0%

Shiro主要用来进行权限管理。简单的介绍如下:

一、概念

Shiro是一个安全框架,可以进行角色、权限管理。

Shiro主要功能如下:
Authentication(认证):用户身份识别,通常被称为用户“登录”
Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
二、主要的类

1.Subject:当前用户,Subject可以是一个人,也可以是第三方服务
2.SecurityManager:管理所有Subject,可以配合内部安全组件。

3.principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
4.credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
最常见的principals和credentials组合就是用户名/密码了。

5.Realms:用于进行权限信息的验证,需要自己实现。
6.Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。
在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

7.SimpleHash,可以通过特定算法(比如md5)配合盐值salt,对密码进行多次加密。

 三、Shiro配置

1.Spring集成Shiro一般通过xml配置,SpringBoot集成Shiro一般通过java代码配合@Configuration和@Bean配置。

2.Shiro的核心通过过滤器Filter实现。Shiro中的Filter是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。

3.SpringBoot集成Shiro,我们需要写的主要是两个类,ShiroConfiguration类,还有继承了AuthorizingRealm的Realm类

ShiroConfiguration类,用来配置Shiro,注入各种Bean。

包括过滤器(shiroFilter)、安全事务管理器(SecurityManager)、密码凭证(CredentialsMatcher)、aop注解支持(authorizationAttributeSourceAdvisor)等等

Realm类,包括登陆认证(doGetAuthenticationInfo)、授权认证(doGetAuthorizationInfo)

四、具体示例如下:

ShiroConfiguration.java如下:

复制代码

package com.example.demo.config; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.Cookie; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; /** * Created by lenovo on 三月 */ @Configuration public class ShiroConfiguration {

@Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
     ShiroFilterFactoryBean shiroFilterFactoryBean\=new ShiroFilterFactoryBean(); //设置安全管理器

shiroFilterFactoryBean.setSecurityManager(securityManager); //默认跳转到登陆页面
shiroFilterFactoryBean.setLoginUrl(“/login”); //登陆成功后的页面
shiroFilterFactoryBean.setSuccessUrl(“/index”);
shiroFilterFactoryBean.setUnauthorizedUrl(“/403”); //自定义过滤器
Map<String,Filter> filterMap=new LinkedHashMap<>();
shiroFilterFactoryBean.setFilters(filterMap); //权限控制map
Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>(); // 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put(“/static/**“, “anon”); //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put(“/logout”, “logout”); // //:这是一个坑呢,一不小心代码就不好使了; // // // filterChainDefinitionMap.put(“/**“, “anon”);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean;
} /** * 核心的安全事务管理器
* @return
*/ @Bean public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager (); //设置realm
securityManager.setRealm( myShiroRealm( ) );
securityManager.setRememberMeManager(rememberMeManager()); return securityManager;
} /** * 身份认证Realm,此处的注入不可以缺少。否则会在UserRealm中注入对象会报空指针.
* @return
*/ @Bean public UserRealm myShiroRealm( ){
UserRealm myShiroRealm = new UserRealm();
myShiroRealm.setCredentialsMatcher( hashedCredentialsMatcher() ); return myShiroRealm;
} /** * 哈希密码比较器。在myShiroRealm中作用参数使用
* 登陆时会比较用户输入的密码,跟数据库密码配合盐值salt解密后是否一致。
* @return
*/ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(“md5”);//散列算法:这里使用md5算法;
hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5( md5(“”));
return hashedCredentialsMatcher;
} // //注入缓存 // @Bean // public EhCacheManager ehCacheManager(){ // System.out.println(“ShiroConfiguration.getEhCacheManager()执行”); // EhCacheManager cacheManager=new EhCacheManager(); // cacheManager.setCacheManagerConfigFile(“classpath:config/ehcache-shiro.xml”); // return cacheManager; // }

/\*\* \*  开启shiro aop注解支持.
 \*  使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
 \* @param securityManager
 \* @return
 \*/ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor \= new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor;
} /\*\* \* Shiro生命周期处理器
 \* @return
 \*/ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor();
} /\*\* \* 自动创建代理
 \* @return
 \*/ @Bean
@DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
    DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator \= new DefaultAdvisorAutoProxyCreator();
    advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator;
}

}

复制代码

Realm类如下:

复制代码

package com.example.demo.config; import com.example.demo.pojo.SysPermission; import com.example.demo.pojo.SysUserRole; import com.example.demo.pojo.User; import com.example.demo.service.UserSerevice; import com.example.demo.utils.State; import org.apache.log4j.Logger; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static org.apache.coyote.http11.Constants.a; /** * Created by lenovo on 三月 */
public class UserRealm extends AuthorizingRealm {
@Resource(name = “userServiceImp”) private UserSerevice userService; private Logger logger=Logger.getLogger(UserRealm.class); /** * 提供用户信息,返回权限信息
* @param principals
* @return
*/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
logger.info(“—————————->授权认证:”);
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
String userName=(String) principals.getPrimaryPrincipal();
String userId=userService.findUserIdByName(userName);
Set roleIdSet=userService.findRoleIdByUid( Integer.parseInt(userId) );
Set roleSet=new HashSet<>();
Set pemissionIdSet=new HashSet<>();
Set pemissionSet=new HashSet<>(); for(SysUserRole roleInfo : roleIdSet) { int roleId=roleInfo.getRoleId();
roleSet.add( userService.findRoleByRoleId( roleId ) ); //将拥有角色的所有权限放进Set里面,也就是求Set集合的并集 //由于我这边的数据表设计得不太好,所以提取set集合比较麻烦
pemissionIdSet.addAll( userService.findPermissionIdByRoleId( roleId ));
} for(int permissionId : pemissionIdSet) {
String permission= userService.findPermissionById( permissionId ).getPermission() ;
pemissionSet.add( permission );
} // 将角色名称组成的Set提供给授权info
authorizationInfo.setRoles( roleSet ); // 将权限名称组成的Set提供给info
authorizationInfo.setStringPermissions(pemissionSet); return authorizationInfo;
} /** * 提供帐户信息,返回认证信息
* @param authenticationToken
* @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
logger.info(“—————————->登陆验证:”);
String userName=(String)authenticationToken.getPrincipal();
User user=userService.findUserByName(userName); if(user==null) { //用户不存在就抛出异常
throw new UnknownAccountException();
} if( State.LOCKED.equals( user.getState() ) ) { //用户被锁定就抛异常
throw new LockedAccountException();
} //密码可以通过SimpleHash加密,然后保存进数据库。 //此处是获取数据库内的账号、密码、盐值,保存到登陆信息info中
SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()) ,
getName()); //realm name

    return authenticationInfo;
}

}

复制代码

更具体的代码,参见GitHub:

https://github.com/firefoxer1992/SpringBootProject

参考博客:

http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html

盐值及密码的加密,可以参考博客:

https://blog.csdn.net/qq_31080089/article/details/53715910