shiro入门

  • Post author:
  • Post category:其他




shiro简介



什么是shiro

Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。Shiro是Apache软件基金会的一个开源项目。Shiro可以轻松地保护任何应用程序,从命令行应用程序到最大的企业Web应用程序。



shiro三大主体

Shiro的三大主体分别是Subject、SecurityManager、Realm。


Subject

:主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;所有Subject都绑定到SecurityManager上,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;

Subject中比较常用的方法为:

方法 作用
login(AuthenticationToken token) 登录认证,通过传入合法的认证令牌(AuthenticationToken)进行认证,如果认证成功,则返回true。
logout() 登出,清空当前用户的凭证信息并退出登录。
isAuthenticated() 判断当前用户是否已经通过认证,如果已经通过认证,则返回true。
isRemembered() 判断当前用户是否已经记住登录状态,如果已经记住登录状态,则返回true。
getPrincipal() 获取当前用户的身份标识,可以是用户名、邮箱、手机号等。
getPrincipals() 获取当前用户的身份集合,可以包含多个身份标识。
getSession() 获取当前用户的会话对象(Subject.Session)。
getSession(boolean create) 获取当前用户的会话对象,如果当前用户已经有会话,则返回已有的会话对象;如果当前用户没有会话,则创建一个新的会话对象。
hasRole(String roleName) 判断当前用户是否拥有指定角色,如果拥有指定角色,则返回true。
hasRoles(Collection roleNames) 判断当前用户是否拥有指定角色集合中的所有角色,如果都拥有,则返回true。
isPermitted(String permission) 判断当前用户是否拥有指定权限,如果拥有,则返回true。
isPermittedAll(Collection permissions) 判断当前用户是否拥有指定权限集合中的所有权限,如果都拥有,则返回true。


SecurityManager

:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro框架的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器。

SecurityManager提供了以下常用方法:

方法 作用
authenticate(AuthenticationToken) 用于进行身份认证,接收一个AuthenticationToken参数,返回认证后的Subject对象。如果认证失败,会抛出相应的异常。
logout(Subject) 用于注销用户登录信息,接收一个Subject参数,将当前用户的登录信息清除。
createSubject(SubjectContext) 用于创建一个Subject对象,接收一个SubjectContext参数,返回创建成功的Subject对象。
hasRole(PrincipalCollection, String) 判断当前用户是否拥有某个角色,接收一个PrincipalCollection参数和一个角色名字符串参数,返回一个布尔值表示是否拥有该角色。
isPermitted(PrincipalCollection, String) 判断当前用户是否拥有某个权限,接收一个PrincipalCollection参数和一个权限名字符串参数,返回一个布尔值表示是否拥有该权限。
getRealms() 获取当前SecurityManager中配置的Realm对象列表,返回一个List对象。
setRealms(List): 设置当前SecurityManager中的Realm对象列表,接收一个List对象作为参数。
setSessionManager(SessionManager) 设置Session管理器,接收一个SessionManager对象作为参数。


Realm

:域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。



shiro的引入

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>1.10.0</version>
</dependency>



springboot整合shiro的基本配置

@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);//设置安全管理器
    shiroFilterFactoryBean.setLoginUrl("/login");//默认登录的页面
    shiroFilterFactoryBean.setSuccessUrl("/index");//登录成功返回的页面
    shiroFilterFactoryBean.setUnauthorizedUrl("/403");//没有权限访问的页面
    //设置
    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    filterChainDefinitionMap.put("/static/**", "anon");
    filterChainDefinitionMap.put("/authc/**", "authc");
    filterChainDefinitionMap.put("/**", "user");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;
}

@Bean
public SecurityManager getSecurityManager(Realm realm) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(realm);
    return securityManager;
}

@Bean
public ShiroRealm getRealm() {
    return new ShiroRealm();
}

}

springboot在整合shiro中是固定的三个步骤,首先就是要先创建一个ShiroFilterFactoryBean来设置SecurityManager以及过滤器,再创建一个SecurityManager的bean来设置Realm,之后在把自己自定义的Realm设置成bean。其中有一点需要注意的就是在设置

ShiroFilterFactoryBean

中的

filterChainDefinitionMap

的过滤链的时候最好使用

LinkedHashMap

,要是使用

HashMap

的话可能会出现一些问题。



shiro中常用的Filter

shiro的常用的系统自定义的Filter根据不同的设置来调用不同的过滤器,如下所示:

过滤器 对应的过滤器类 作用
authc FormAuthenticationFilter 需要认证登录才能访问
user UserFilter 用户拦截器,表示必须存在用户
anon AnonymousFilter 匿名拦截器,不需要登录即可访问的资源,匿名用户或游客,一般用于过滤静态资源
roles RolesAuthorizationFilter 需要指定角色才能访问
port PortFilter 需要指定端口才能访问
perms PermissionsAuthorizationFilter 需要指定权限才能访问
logout LogoutFilter 登出过滤器,配置指定url就可以实现退出功能
authcBasic BasicHttpAuthenticationFilter 指定url需要basic登录
rest HttpMethodPermissionFilter 将http请求方法转化成相应的动词来构造一个权限字符串
ssl SslFilter 需要https请求才能访问
noSessionCreation NoSessionCreationFilter 禁止创建会话

shiro在DefaultFilter这个枚举类之中所定义的各种各样的过滤器,其中最常用的也就是authc,anon,在登出的时候或许也会设置logout这个过滤器,而这些过滤器也就是在设置

ShiroFilterFactoryBean

中的setFilterChainDefinitionMap方法过滤链所用到的。当然也可以自定义实现shiro中的过滤器,通常来说,在自定义实现过滤器都是继承的FormAuthenticationFilter或者AuthenticatingFilter这个类进行。但是自定义过滤器一般是加入一些其他的东西才会用到,比如用jwt所生成的token来代替shiro自带的。



自定义Realm

Shiro不是直接处理身份验证和权限控制的,而是通过与应用程序集成的Realm实现。Realm是Shiro身份验证和授权查询的后端数据源,它们通常与本地数据存储库(例如数据库)或远程数据存储库(例如LDAP)一起工作。Shiro具有一组默认的Realm,但您可以选择编写自定义Realm以处理自己的身份验证和授权数据源。

自定义Realm可以满足具体的业务需求,例如:

  1. 集成不同类型的数据源:Shiro默认支持JDBC和LDAP等数据源,但是如果您的应用程序需要使用其他类型的数据源(例如NoSQL数据库或REST API),则需要编写自定义Realm。

  2. 更好的性能:默认Realm需要处理大量权限控制逻辑,如果您想要更好的性能表现,则自定义Realm可以优化访问控制逻辑。

  3. 更好的安全性:默认Realm中的身份验证和授权逻辑是通用的,可能存在一些风险,自定义Realm可以根据具体的业务情况定制相应的安全策略。

需要注意的是,自定义Realm需要自己编写相应的身份验证和授权逻辑,并与应用程序进行整合。

public class ShiroRealm implements AuthorizingRealm {
    @Override
public String getName() {
    return "ShiroRealm";
}
//判断当前Realm是否支持某种类型的Token
@Override
public boolean supports(AuthenticationToken token) {
    return token instanceof UsernamePasswordToken;
}
//认证
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String username = (String) token.getPrincipal();
    String password = new String((char[]) token.getCredentials());
    // 根据用户名和密码查询用户信息
    User user = userService.getUserByUsernameAndPassword(username, password);
    if (user == null) {
        throw new UnknownAccountException("用户名或密码错误");
    }
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
    return authenticationInfo;
}
//授权
@Override
public AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
    String username = (String) principals.getPrimaryPrincipal();
    // 根据用户名查询用户角色和权限信息
    Set<String> roles = userService.getRolesByUsername(username);
    Set<String> permissions = userService.getPermissionsByUsername(username);
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.setRoles(roles);
    authorizationInfo.setStringPermissions(permissions);
    return authorizationInfo;
}
}

AuthorizingRealm是Shiro所提供的一个基于权限控制的Realm实现,它也是Shiro推荐的默认实现方式。与其他的Realm不同的是,AuthorizingRealm不仅仅具有验证身份的功能,还能通过实现doGetAuthorizationInfo()方法来完成授权操作,即根据用户的角色、权限等信息来决定用户是否拥有访问某个资源的权限。因此,如果开发者需要在自定义Realm中实现授权功能,那么继承AuthorizingRealm是一个非常合适的选择。同时,Shiro也为开发者提供了其他的Realm实现,例如:JdbcRealm、IniRealm等等,可以根据具体业务需求来选择使用。



shiro使用redis做缓存

在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

在引入redis的依赖之后则需要配置这三项:

spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0

如果redis有密码的话,则需要通过

spring.data.redis.password

来配置密码进行连接。之后则是要来自定义自己的Cache和CacheManager进行操作。

public 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("get = " + k);
        return (v) getRedisTemplate().opsForHash().get(this.CacheName,k.toString());
    }

    @Override
    public v put(k k, v v) throws CacheException {
        System.out.println("k = " + k+",v="+v);
        getRedisTemplate().opsForHash().put(this.CacheName,k.toString(),v);
        return null;
    }

    @Override
    public v remove(k k) throws CacheException {
         getRedisTemplate().opsForHash().delete(this.CacheName,k);
        return null;
    }

    @Override
    public void clear() throws CacheException {
        getRedisTemplate().opsForHash().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);
    }
    public RedisTemplate getRedisTemplate(){
        RedisTemplate redisTemplate =(RedisTemplate) ApplicationContextUtils.getApplicationContext().getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
      redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
public 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);
    }
}

在通过在配置类里面注册的Realm的bean,来将自定义Cache和CacheManager设置到所需要的Realm中。

@Bean
   public ShiroRealm shiroRealm(){
    ShiroRealm shiroRealm = new ShiroRealm();
    shiroRealm.setCacheManager(new RedisCacheManager());//设置缓存管理器
    shiroRealm.setAuthenticationCacheName("shiroRealmCache");
    shiroRealm.setAuthorizationCacheName("shiroRealmCache");
    shiroRealm.setAuthenticationCachingEnabled(true);
    shiroRealm.setCachingEnabled(true);
    return shiroRealm;
   }



版权声明:本文为lixin203171613原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。