springBoot + shiro

一、pom

<!--shiro-->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-core</artifactId>
   <version>1.2.3</version>
</dependency>
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-ehcache</artifactId>
   <version>1.2.3</version>
</dependency>
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-web</artifactId>
   <version>1.2.3</version>
</dependency>
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.2.3</version>
</dependency>

二、shiro关键名称

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web支持,可以非常容易的集成到Web环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;

SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据(这就是为什么应用能够判断访问的用户有没有权限进行其他的操作。在用户登录后Realm会将这个用户的权限和这个用的session做好关联,每当用户访问其他接口shiro都能从session中获取这个用户的权限并且与访问的当前接口的权限进行比较);这样的话,比如我们在Web环境用,刚开始是一台Web服务器;接着又上了台EJB服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到Memcached服务器);

SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到Memcached中,可以实现自己的Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能

Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

参考:https://jinnianshilongnian.iteye.com/blog/2018936

三、userRealm

**
 * @program: maque
 * @description: shiro用户认证,授权
 * @author: maque
 * @create: 2019-08-01 15:24
 */
public class UserRealm extends AuthorizingRealm {
​
    private Logger loger = LoggerFactory.getLogger(getClass());
    @Autowired
    private SystemAdminService systemAdminService;
​
    @Autowired
    private SystemRoleService systemRoleService;
​
    @Autowired
    private SystemPermissionService systemPermissionService;
​
    @Autowired
    private RedisSevice redisSevice;
    /**
     * 给用户授权限
     * 此方法是shiro留给开发人员实现给用户授权使用。
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String userName = (String)principals.getPrimaryPrincipal();
        loger.info("doGetAuthorizationInfo,username:{"+userName+"}");
        //权限交互对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //首先从缓存中查询角色信息,如果不存在从数据库中查询
        List<SystemRole> rlist=systemRoleService.findRoleListByUserName(userName);
        Set roleSet= new HashSet<String>();
        StringBuffer roleNames=new StringBuffer("");
        if (rlist != null || rlist.size()>0){
            //从数据库查找用户的角色
​
            if (rlist!=null && rlist.size()>0){
                int i=0;
                for (SystemRole role:rlist){
                    roleSet.add(role.getId());
                    roleNames.append(role.getRoleName());
                    if (i!=(rlist.size()-1)){
                        roleNames.append(",");
                    }
                    i++;
                }
            }
        }
        //将用户角色放置到授权认证对象中
        authorizationInfo.setRoles(roleSet);
        //从缓存中查找用户权限列表
        Set permissionSet= new HashSet<String>();
        List<SystemPermissionEntity> plist=systemPermissionService.findAllPermissionByRoleIds(new ArrayList<String>(roleSet));
        if (plist!=null && plist.size()>0){
            for(SystemPermissionEntity per:plist){
                permissionSet.add(per.getPermissionCode());
            }
        }
        //将用户权限放置到授权认证对象
        authorizationInfo.setStringPermissions(permissionSet);
        return authorizationInfo;
    }
​
    /**
     * 身份验证
     * 调用subject.login(token)方法时会调用doGetAuthenticationInfo方法
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken loginToken = (UsernamePasswordToken) token;
        String userName = loginToken.getUsername();
        String password = String.copyValueOf(loginToken.getPassword());
        SystemAdminEntity admin = systemAdminService.selectAdminByUserPassword(userName, Md5Encrypt.md5(password));
        if (admin == null){
            loger.info("=======用户名及密码错误{"+userName+"}========");
            //位置异常
            throw new UnknownAccountException("用户名及密码错误");
        }
        if (Contants.is_OPEN_N==admin.getIsOpen()){
            loger.info("=======用户被锁定{"+userName+"}========");
            //锁定异常
            throw new LockedAccountException();
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(admin.getUserName(),admin.getPassword(),getName());
        //redisSevice.setCacheData(Contants.CACHE_ADMIN_USERNAME_KEY+admin.getUserName(),admin,24);
        //redisSevice.setCacheData(Contants.CACHE_ADMINUSERNAME_KEY+admin.getId(),admin,24);
        return authenticationInfo;
    }
}

四、MyShiroConfig

/**
 * @program: maque
 * @description: shiro过滤器
 * @author: maque
 * @create: 2019-04-29 21:14
 */
@Configuration
public class MyShiroConfig {
​
    /**
     * 注册shiroFilter
     * @return
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("shiroFilterFactoryBean");
        delegatingFilterProxy.setTargetFilterLifecycle(true);
        filterRegistration.setFilter(delegatingFilterProxy);
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
        return filterRegistration;
    }
​
    /**定义shiroFilter过滤器并注入securityManager
     * 创建ShiroFilterFactoryBean
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean(name="shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
         * anon:表示可以匿名使用。
           authc:表示需要认证(登录)才能使用,没有参数
         */
        Map<String, String> sMap = new HashMap<String, String>();
        //拦截页面
        sMap .put("/test/authc.action", "authc");
        //匿名访问,即所有人都可以访问
        sMap .put("/test/login.action", "anon");
        sMap .put("/test/goLogin.action", "anon");
        sMap .put("/test/unAuthorized.action","anon");
        //被拦截返回登录页面
        shiroFilterFactoryBean.setLoginUrl("/test/goLogin.action");
        //授权拦截返回页面 权限不足跳转页面,此处设置不起作用,当权限布够的时候会抛出异常,并没有返回到想要         //的页面。后面会有响应的解决方法
        shiroFilterFactoryBean.setUnauthorizedUrl("/test/unAuthorized.action");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(sMap);
        return shiroFilterFactoryBean;
​
    }
​
​
    /**定义安全管理器securityManager,注入自定义的realm
     * 创建DefaultWebSecurityManager
     * @param userRealm
     * @return
     */
    @Bean(name="defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
​
    }
​
    /**
     * 密码校验规则HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的 ,
     * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
​
        //指定加密方式为MD5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //加密次数,要和数据库中密码加密次数保持一致。
        credentialsMatcher.setHashIterations(1);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }
​
    /**生成用户认证,授权 现实对象,并注入注入自定义的加密方式
     * 创建Realm
     * @return
     */
    @Bean(name="userRealm")
    public UserRealm getUserRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setAuthorizationCachingEnabled(true);
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return userRealm;
    }
​
​
​
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
​
    /**
     * *
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * *
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
​
    /**
     * 开启shiro注解
     * @param defaultWebSecurityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }
//参考 https://www.cnblogs.com/bbthome/p/8688849.html
​
    /**
     * 解决 没有权限时并没有跳转shiroFilterFactoryBean.setUnauthorizedUrl("/test/unAuthorized.action");
     * 而是抛出org.apache.shiro.authz.AuthorizationException异常问题
     * 自己来处理UnauthorizedException异常
     * @return
     */
    @Bean
    public SimpleMappingExceptionResolver shiroUnAuthorizedresolver() {
        SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        //unAuthorized是页面不是请求接口
        properties.setProperty("org.apache.shiro.authz.AuthorizationException", "unAuthorized");
        resolver.setExceptionMappings(properties);
        return resolver;
    }
​
}

五、测试

/**
 * 测试shiroFilterFactoryBean.setLoginUrl("/test/goLogin.action");
 */
@RequestMapping("/goLogin")
public String goLogin(){
    return "index";
}
​
@ResponseBody
@RequestMapping("/login")
public ResultResponBody login(String userName,String password){
    ResultResponBody result = new ResultResponBody();
    UsernamePasswordToken token=null;
    String msg = "";
    try {
        Subject subject  = SecurityUtils.getSubject();
        token = new UsernamePasswordToken(userName,password);
        token.setRememberMe(true);
        //用户认证,通过自定义的 UserRealm 来实现用户认证即未用户授权,shiro会将用户的权限信息将会和             //session做关联,
        subject.login(token);
        if (subject.isAuthenticated()){
            result.setResult(true);
            return result;
        }else {
            result.setResult(false);
            result.setMessage("登录失败");
            return result;
        }
    } catch (IncorrectCredentialsException e) {
        e.printStackTrace();
        result.setResult(false);
        msg = "登录密码错误. Password for account " + token.getPrincipal() + " was incorrect.";
    }catch (ExcessiveAttemptsException e) {
        result.setResult(false);
        msg = "登录失败次数过多";
    } catch (LockedAccountException e) {
        result.setResult(false);
        msg = "帐号已被锁定. The account for username " + token.getPrincipal() + " was locked.";
    } catch (DisabledAccountException e) {
        result.setResult(false);
        msg = "帐号已被禁用. The account for username " + token.getPrincipal() + " was disabled.";
    } catch (ExpiredCredentialsException e) {
        result.setResult(false);
        msg = "帐号已过期. the account for username " + token.getPrincipal() + "  was expired.";
    } catch (UnknownAccountException e) {
        result.setResult(false);
        if (!StringUtils.isEmpty(userName)) {
            msg = "帐号不存在. There is no user with username of " + token.getPrincipal();
        }
    } catch (UnauthorizedException e) {
        msg = "您没有得到相应的授权!" + e.getMessage();
    }
    result.setMessage(msg);
    return result;
}
​
/**
 * 测试权限
 */
@RequiresPermissions(value = {"system.oc.c.j.q"})
@RequestMapping("/authc")
public String gouserManage(){
    return "user/userManage";
}
​
/**
 * 权限布够要跳转的接口
 * shiroFilterFactoryBean.setUnauthorizedUrl("/test/unAuthorized.action");布跳转这个问题有针对的解决方法
 * @return
 */
@RequestMapping("/unAuthorized")
public String unAuthorizedUrl(){
    return "unAuthorized";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值