转载

Live-Server-4-Shiro的简单使用

Apache Shiro是Java的一个安全框架,主要完成:认证、授权、加密、会话管理、缓存等功能,而且API简单,越来越多人使用该框架。

Shiro介绍

基本功能如下:

Live-Server-4-Shiro的简单使用
  1. Authentications:身份认证/登录,验证用户是否有对应的身份。
  2. Authorization:授权,判断用户是否拥有某个权限,比如当前用户是否拥有后台管理员管理的权限等。
  3. SessionManager:会话管理,用户登录后的一次会话,在还没退出之前,都是通过会话来识别用户。
  4. Cryptography:加密,保证数据的安全性。
  5. Web Support:便于集成到Web环境中。
  6. Cacheing:缓存,保存用户登录、权限等信息。
Live-Server-4-Shiro的简单使用
Shrio api核心是Subject。每个 Subject 表示一个主体,可以是一个用户,也可以是一个请求。所有的Subject都会绑定到SecurityManager中,通过SecurityManager.getSubject()方法,可以获取到当前用户的subject。 SecurityManager :安全管理器;即所有与安全有关的操作都会与SecurityManager 交互; 且它管理着所有Subject。而SecurityManager是如何判断用户的身份和权限呢?这时就要引入 Realm

,域的概念,Shiro从Realm中获取安全数据(如权限等),提供给SecurityManager来判断用户的权限。

Shiro使用

通过上述的简介,可以得知,如果我们要对用户分不同的权限,如管理员、普通用户;要对会话进行管理,就需要用到Shiro这个安全框架。Shiro在SpringBoot中的使用就需要创建一个配置类,同时要配置Realm。

一、Shiro配置类

Shiro配置类中,需要配置生命周期处理器、SecurityManager,在该安全管理器中配置Realm、ShiroFilter过滤器、aop注解支持等。

/**
 * spring-boot集成shiro配置类
 */
@Configuration
public class ShiroConfiguration {
    /**
     * 实例化lifecycleBeanPostProcessor
     * shiro生命周期处理器
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 实例化DefaultAdvisorAutoProxyCreator
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    /**
     * 实例化自定义realm
     *
     * @return
     */
    @Bean
    public Realm getRealm() {
        return new UserRealm();
    }

    /**
     * 实例化securityManager
     *
     * @return
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(getRealm());
        return securityManager;
    }

    /**
     * 实例化shiroFilter
     *
     * @return
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/admin/403");
        return shiroFilterFactoryBean;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
复制代码

二、Realm

一个应用中可以有一个或者多个Realm,可以通过JDBC、内存来实现。在该系列文章中Server-2 MyBatis逆向生成中,观察仔细的朋友会发现,我创建了permission、role、role-permission三张数据表,分别表示了权限类型、角色类型、角色所拥有的权限这三种含义。这三张表与Realm之间有什么关系呢?在项目中创建了UserRealm用户认证、授权接口。具体代码如下:

public class UserRealm extends AuthorizingRealm {
    private final String realmName = "UserRealm.class";
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PermissionService permissionService;
    @Autowired
    private RoleMapper roleMapper;

    //授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Object principal = principals.getPrimaryPrincipal();
        try {
            if (principal instanceof String) {
                UserExample example = new UserExample();
                UserExample.Criteria criteria = example.createCriteria();
                criteria.andAccountEqualTo((String) principal);
                Integer roleId = userMapper.selectByExample(example).get(0).getRoleId();
                SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
                Role role = roleMapper.selectByPrimaryKey(roleId);
                info.addRole(role.getName());
                Set<PermissionVO> permissions = permissionService.getPermissionsBy(roleId);
                for (PermissionVO permission : permissions) {
                    info.addStringPermission(permission.getName());
                }
                return info;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        Object principal = token.getPrincipal();
        if (principal instanceof String) {
            String phone = (String) principal;
            UserExample example = new UserExample();
            UserExample.Criteria criteria = example.createCriteria();
            criteria.andAccountEqualTo(phone);
            List<User> users = userMapper.selectByExample(example);
            if (null != users && users.size() > 0) {
                User user = users.get(0);
                return new SimpleAccount(user.getAccount(), user.getPassword(), realmName);
            }
        }
        return null;
    }
}
复制代码

在UserRealm中,可以看出,其实认证和授权都是通过数据库的数据来进行判断,比如认证,其实就是通过MyBatis来查找数据库中是否存在该用户;授权方法,则是传入用户的账号,查看该用户的角色,再查找该角色所拥有的权限,将账号、角色、权限信息存储到AuthorizationInfo中,即完成用户授权。

三、用户认证

下面几行简单的代码就完成了用户的认证。

Subject subject = SecurityUtils.getSubject();   //获取Subject
AuthenticationToken token = new UsernamePasswordToken(account, MD5Util.encrypt(password)); //根据用户账号、密码获取令牌。
try{
    subject.login(token);
}catch( ..){
  ...
}
复制代码

首先,第一行代码,getSubject(),这里有个巨坑,我怀疑getSubject()这个方式是根据请求中是否带有cookie,来判断是新的subject还是已经存在的subject,因为如果没有带cookie的请求,每次获取的subject都不一致。如果Subject不唯一,那么会导致Session会话也不唯一,那么session保存的键值对(通常是一些状态、参数)就无法使用,导致功能的异常。 如果登录(用户认证)失败,那么就会抛出不同的异常,比如UnknownAccountException(账号不存在)、IncorrectCredentialsException(账号密码不匹配)、LockedAccountException(不允许登录)等。

原文  https://juejin.im/post/5d5d3a866fb9a06ad54722df
正文到此结束
Loading...