SpringBoot2.0集成Shiro

目录

SpringBoot2.0集成Shiro

一、使用版本

二、导springboot 和shiro的包

三、开始简单配置

1.最基本的配置:

  shiroConfig配置

           customRealm配置:

 controller类:新建一个HomeIndexController类,加入如下代码:

四、利用注解配置权限:

五、密码采用加密方式进行验证:

六、用户信息储存:



SpringBoot2.0集成Shiro

本文来源于以下几个链接资源整合,自己也实验过,可行,理解简单,保存以后方便回来看。

参考:https://blog.csdn.net/bicheng4769/article/details/86668209

           https://blog.csdn.net/java_chegnxuyuan/article/details/90901314

          https://www.cnblogs.com/HowieYuan/p/9259638.html

          https://blog.csdn.net/bicheng4769/article/details/86680955

一、使用版本

      springboot版本:2.0.5.RELEASE

      shiro:1.3.2

二、导springboot 和shiro的包

        springboot的包省略,建好项目后,选对版本,会自动生成
         <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>

三、开始简单配置

写在前面的话:
springboot中集成shiro相对简单,只需要两个类:一个是shiroConfig类,一个是CustonRealm类。

ShiroConfig类:
顾名思义就是对shiro的一些配置,相对于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。

CustomRealm类:
自定义的CustomRealm继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法。

1.最基本的配置:

  shiroConfig配置

package com.cj.shirodemo.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.DefaultSecurityManager;
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.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 描述:
 *
 * @author caojing
 * @create 2019-01-27-13:38
 */
@Configuration
public class ShiroConfig {

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager); //这个属性是必须的,配置为securityManager就好了。
        //注意,配置了全局异常后,下面三个链接无效,异常会被全局异常拦截。
        shiroFilterFactoryBean.setLoginUrl("/login/201");                   //没有登录的用户请求,跳转的路径。
        shiroFilterFactoryBean.setUnauthorizedUrl("/login/202");    //没有权限时,跳转的路径
        shiroFilterFactoryBean.setSuccessUrl("/login/200");                 //登录成功默认跳转路径,不配置则跳转至”/”
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/front/**", "anon");
        filterChainDefinitionMap.put("/api/**", "anon");

        filterChainDefinitionMap.put("/admin/**", "authc");
        filterChainDefinitionMap.put("/user/**", "authc");
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截 剩余的都需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(customRealm());
        return defaultSecurityManager;
    }

    @Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        return customRealm;
    }
}

解释:

shiroConfig 也不复杂,基本就三个方法。再说这三个方法之前,我想给大家说一下shiro的三个核心概念:

Subject: 代表当前正在执行操作的用户,但Subject代表的可以是人,也可以是任何第三方系统帐号。当然每个subject实例都会被绑定到SercurityManger上。
SecurityManger:SecurityManager是Shiro核心,主要协调Shiro内部的各种安全组件,这个我们不需要太关注,只需要知道可以设置自定的Realm。
Realm:用户数据和Shiro数据交互的桥梁。比如需要用户身份认证、权限认证。都是需要通过Realm来读取数据。
shiroFilter方法:
这个方法看名字就知道了:shiro的过滤器,可以设置登录页面(setLoginUrl)、权限不足跳转页面(setUnauthorizedUrl)、具体某些页面的权限控制或者身份认证。
注意:这里是需要设置SecurityManager(setSecurityManager)。
默认的过滤器还有:anno、authc、authcBasic、logout、noSessionCreation、perms、port、rest、roles、ssl、user过滤器。
具体的大家可以查看package org.apache.shiro.web.filter.mgt.DefaultFilter。这个类,常用的也就authc、anno。

securityManager 方法:
查看源码可以知道 securityManager是一个接口类,我们可以看下它的实现类:

具体怎么实现的,感兴趣的同学可以看下。由于项目是一个web项目,所以我们使用的是DefaultWebSecurityManager ,然后设置自己的Realm。
CustomRealm 方法:
将 customRealm的实例化交给spring去管理,当然这里也可以利用注解的方式去注入。

customRealm配置:

package com.cj.shirodemo.config;

import org.apache.shiro.SecurityUtils;
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 org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

/**
 * 描述:
 *
 * @author caojing
 * @create 2019-01-27-13:57
 */
public class CustomRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> stringSet = new HashSet<>();
        stringSet.add("user:show");
        stringSet.add("user:admin");
        info.setStringPermissions(stringSet);
        return info;
    }

    /**
     * 这里可以注入userService,为了方便演示,我就写死了帐号了密码
     * private UserService userService;
     * <p>
     * 获取即将需要认证的信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("-------身份认证方法--------");
        String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());
        //根据用户名从数据库获取密码
        String password = "123";
        if (userName == null) {
            throw new AccountException("用户名不正确");
        } else if (!userPwd.equals(password )) {
            throw new AccountException("密码不正确");
        }
        return new SimpleAuthenticationInfo(userName, password,getName());
    }
}

说明:
自定义的Realm类继承AuthorizingRealm类,并且重载doGetAuthorizationInfo和doGetAuthenticationInfo两个方法。
doGetAuthorizationInfo: 权限认证,即登录过后,每个身份不一定,对应的所能看的页面也不一样。
doGetAuthenticationInfo:身份认证。即登录通过账号和密码验证登陆人的身份信息。

 

controller类:
新建一个HomeIndexController类,加入如下代码:

   @RequestMapping(value = "/login", method = RequestMethod.GET)
    @ResponseBody
    public String defaultLogin() {
        return "首页";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
        // 从SecurityUtils里边创建一个 subject
        Subject subject = SecurityUtils.getSubject();
        // 在认证提交前准备 token(令牌)
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        // 执行认证登陆
        try {
            subject.login(token);
        } catch (UnknownAccountException uae) {
            return "未知账户";
        } catch (IncorrectCredentialsException ice) {
            return "密码不正确";
        } catch (LockedAccountException lae) {
            return "账户已锁定";
        } catch (ExcessiveAttemptsException eae) {
            return "用户名或密码错误次数过多";
        } catch (AuthenticationException ae) {
            return "用户名或密码不正确!";
        }
        if (subject.isAuthenticated()) {
            return "登录成功";
        } else {
            token.clear();
            return "登录失败";
        }
    }

测试:

我们可以使用postman进行测试:

ok 身份认证是没问题了,我们再来考虑如何加入权限

四、利用注解配置权限:

其实,我们完全可以不用注解的形式去配置权限,因为在之前已经加过了:DefaultFilter类中有perms(类似于perms[user:add])这种形式的。但是试想一下,这种控制的粒度可能会很细,具体到某一个类中的方法,那么如果是配置文件配,是不是每个方法都要加一个perms?但是注解就不一样了,直接写在方法上面,简单快捷。
很简单,主需要在config类中加入如下代码,就能开启注解:
 

@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;
    }
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

新建一个UserController类。如下:

@RequestMapping("/user")
@Controller
public class UserController {
    @RequiresPermissions("user:list")
    @ResponseBody
    @RequestMapping("/show")
    public String showUser() {
        return "这是学生信息";
    }
}

重复刚才的登录步骤,登录成功后,postman 输入localhost:8080/user/show

确实是没有权限。方法上是 @RequiresPermissions("user:list"),而customRealm中是 user:show、user:admin。我们可以调整下方法上的权限改为user:show。调试一下,发现成功了。
这里有一个问题:当没有权限时,系统会报错,而没有跳转到对应的没有权限的页面,也就是setUnauthorizedUrl这个方法没起作用,这个问题,下一篇会给出解决方案-。-

 

五、密码采用加密方式进行验证:

其实上面的功能已经基本满足我们的需求了,但是唯一一点美中不足的是,密码都是采用的明文方式进行比对的。那么shiro是否提供给我们一种密码加密的方式呢?答案是肯定。
shiroConfig中加入加密配置:

	@Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

customRealm初始化的时候耶需要做一些改变:

@Bean
    public CustomRealm customRealm() {
        CustomRealm customRealm = new CustomRealm();
        // 告诉realm,使用credentialsMatcher加密算法类来验证密文
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        customRealm.setCachingEnabled(false);
        return customRealm;
    }

流程是这样的,用户注册的时候,程序将明文通过加密方式加密,存到数据库的是密文,登录时将密文取出来,再通过shiro将用户输入的密码进行加密对比,一样则成功,不一样则失败。
我们可以看到这里的加密采用的是MD5,而且是加密两次(MD5(MD5))。
shiro提供了SimpleHash类帮助我们快速加密:
 

public static String MD5Pwd(String username, String pwd) {
        // 加密算法MD5
        // salt盐 username + salt
        // 迭代次数
        String md5Pwd = new SimpleHash("MD5", pwd,
                ByteSource.Util.bytes(username + "salt"), 2).toHex();
        return md5Pwd;
    }

也就是说注册的时候调用一下上面的方法得到密文之后,再存入数据库。
在CustomRealm进行身份认证的时候我们也需要作出改变:

String userName = (String) authenticationToken.getPrincipal();
        String userPwd = new String((char[]) authenticationToken.getCredentials());

        //根据用户名从数据库获取密码
        String password = "2415b95d3203ac901e287b76fcef640b";

        if (userName == null) {
            throw new AccountException("用户名不正确");
        }
        if (userPwd.length()==0){
            throw new AccountException("密码为空");
        }else {
            String enUserPwd=MD5Pwd(userName,userPwd);
            if(!enUserPwd.equals(password )) {
                throw new AccountException("密码不正确");
            }
        }
        return new SimpleAuthenticationInfo(userName, password,ByteSource.Util.bytes(userName + "salt"), getName());

这里唯一需要注意的是:你注册的加密方式和设置的加密方式还有Realm中身份认证的方式都是要一模一样的。
本文中的加密 :MD5两次、salt=username+salt加密。

再次实验,加密没有问题。shiro做完之后,还可以做一些全局异常,进行拦截失败的请求,更加丰富。

 

六、用户信息储存:

1.作用域

登录成功后,将用户信息存放在作用域中,需要的时候,可以从作用域中取出,如下:

//登录成功后,把当前登录用户放到session中,验证权限的时候,可以session中取出来
        Subject subject = SecurityUtils.getSubject();//得到主体
        Student student = new Student(userName,userPwd);
        subject.getSession().setAttribute("student",student );

//从作用域中取值
Student student = (Student)SecurityUtils.getSubject().getSession().getAttribute("student");

该方式不足:无法解决跨域问题。所欲这里推荐第二种当时。

 

2.JWT加密

登录成功后,将用户信息(id,用户名,密码),通过JWT加密生成token,然后给前端。

访问的时候携带token,将token解密后,便可得到用户信息。

该方式优点:可跨域,不占服务端或者客户端空间,安全。

实现:后期补充。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Spring Boot集成Shiro,你可以按照以下步骤进行操作: 1. 首先,在你的Spring Boot项目中添加Shiro的依赖。你可以在pom.xml文件中添加以下依赖关系: ```xml <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-starter</artifactId> <version>1.7.1</version> </dependency> ``` 2. 创建一个Shiro的配置类,用于配置Shiro的相关组件和属性。可以使用`@Configuration`注解来标记该类作为配置类,并使用`@EnableShiroAnnotation`注解来启用Shiro的注解支持。 ```java @Configuration @EnableShiroAnnotation public class ShiroConfig { // 配置Shiro的相关组件和属性 // ... } ``` 3. 在上述配置类中,可以配置Shiro的Realm、Session管理器、缓存管理器等组件。你可以根据自己的需求选择相应的实现类并进行配置。 ```java @Configuration @EnableShiroAnnotation public class ShiroConfig { @Bean public Realm realm() { // 配置自定义的Realm实现类 // ... return realm; } @Bean public SessionManager sessionManager() { // 配置自定义的Session管理器实现类 // ... return sessionManager; } @Bean public CacheManager cacheManager() { // 配置自定义的缓存管理器实现类 // ... return cacheManager; } // 其他配置项... } ``` 4. 在主配置类中,添加`@Import`注解来引入Shiro的配置类。 ```java @SpringBootApplication @Import(ShiroConfig.class) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } } ``` 5. 在需要进行权限控制的地方,使用Shiro的注解来标记需要进行权限验证的方法或类。例如,可以使用`@RequiresRoles`注解来限制具有特定角色的用户才能访问方法。 ```java @RestController public class YourController { @RequiresRoles("admin") @GetMapping("/admin") public String admin() { return "Hello, admin!"; } } ``` 这样,你就成功地集成了Spring Boot和Shiro,并可以进行基于角色的权限控制了。当然,以上只是一个简单的示例,你可以根据自己的需求进行更详细的配置和使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值