Starter方式实现Springboot和Shiro的集成()

本文详细介绍了如何在Spring Boot项目中集成Apache Shiro框架,包括引入依赖、配置类的开发、安全数据源(Realm)的配置、Shiro过滤器的配置及使用方法。同时,还提供了登录、安全退出和修改密码等功能的实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引入依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.1</version>
</dependency>

Shiro配置类所用对象所属的包

import java.text.MessageFormat;
import java.util.List;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.subject.PrincipalCollection;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import com.tnr.scgcxx.base.CurrUser;
import com.tnr.scgcxx.model.Module;
import com.tnr.scgcxx.model.User;
import com.tnr.scgcxx.shiro.service.ShiroService;

开发集成Shiro配置类

@Configuration//表明这是一个配置类,可以启动组件扫描,用来将带有@bean的实体进行实例化bean等。
//可以理解为配置spring时配置spring容器
public class ShiroConfigure {
	private static final Logger LOG = LoggerFactory.getLogger(ShiroConfigure.class);

注册跨域过滤器

//注册跨域过滤器(注:Shiro过滤器会优先于Springboot拦截请求,所以springboot的跨域设置将不起作用,因此需要专门注册跨域过滤器,并设置其为过滤器链中的第一个)
	@Bean
	public FilterRegistrationBean<CorsFilter> corsFilter(){
		final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		final CorsConfiguration config = new CorsConfiguration();
		 // 允许cookies跨域
        config.setAllowCredentials(true);
        
        // 允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedOrigin("*");
        
        // 允许访问的头信息,*表示全部
        config.addAllowedHeader("*");
        
        // 预检请求(options请求)的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.setMaxAge(1L);
        
        // 允许提交请求的方法,*表示全部允许
        config.addAllowedMethod("*");
        /*
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        */
        //设置允许跨域的路径
        source.registerCorsConfiguration("/**", config);
        
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(new CorsFilter(source));
        
        // 设过滤器的优先级
        bean.setOrder(0);
        return bean;
	}

开发和配置安全数据源(Realm)

/*
	 * 此处必须要命名“authorizer”,
	 *  因为shiro会自动寻找名称为“authorizer”的、并实现接口Authorizer的对象,
	 *  而AuthorizingRealm正是接口Authorizer的实现类
	 * */
	@Bean("authorizer")
    @Autowired
	public AuthorizingRealm saftyRealm(ShiroService shiroService) {
		return new AuthorizingRealm() {
			
			 /**
	            * 获取当事人(当前用户)授权信息,shiro在检查访问是否经过授权时需要通过该方法获取授权信息。
	            * 参数PrincipalCollection 当事人集合
	            */	
			
			@Override
			protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
				/*
	                * 获取登录用户帐号
	                */
				CurrUser currUser = (CurrUser) principals.getPrimaryPrincipal();//获取首要(第一)当事人
				
				 /*
	                * 创建授权信息对象
	             */
				SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
				/*
	                *查询用户权限,并将权限放入授权信息对象中		 
                */
				List<Module> moduleList = shiroService.getModulesOfUser(currUser.getU_id());
                for (Module module : moduleList) {
                    simpleAuthorizationInfo.addStringPermission(String.valueOf(module.getM_id()));
                }
                System.out.println("***********************");
                System.out.println(currUser.getU_id()+"的权限->"+simpleAuthorizationInfo.getStringPermissions());
                System.out.println("***********************");
                /*
                * 返回授权信息
                */
                return simpleAuthorizationInfo;
			}

获取认证信息

/**
	            * 获取认证信息(即包含当前用户帐户和合法密码等信息)
	            * shiro在登录认证时需要通过该方法获取认证信息。
	            * 参数 AuthenticationToken 认证令牌(如:一组用户名和密码就是一个认证令牌)
	            */
			@Override
			protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
					throws AuthenticationException {
				String u_id = (String) token.getPrincipal();// 获得当事人(当前用户账号)
				User user = shiroService.getUser(u_id);
				/*
          		如果不存在前用户信息,返回null
				 */
	            if (user == null) {
	                return null;
	            }
	            /*
	        	创建当前用户
	             */
	            CurrUser currUser = new CurrUser(user.getU_id(),user.getU_name());
	            System.out.println("-------------------");
				System.out.println(currUser.getU_name());
	            /*
	        	创建认证信息,三个构造参数含义依次如下:
	        	参数1:principal当前用户 
	       		参数2:credentials认证凭证(如:口令、密码等)
	 			参数3:realm名称
	             */	
	            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(currUser, user.getU_pwd(), this.getName());
	            System.out.println("************");
	            System.out.println(this.getName());
	            /*
	        	返回认证信息
	             */
	            return info;
			}};
	}

配置Shiro过滤器拦截规则

@Bean @Autowired
	    public ShiroFilterChainDefinition shiroFilterChainDefinition(ShiroService shiroService) {
	        
	        DefaultShiroFilterChainDefinition chainDefinition = 
	                                                    new DefaultShiroFilterChainDefinition();
	        
	    
	        /*
	            可匿名访问资源
	        */
	        chainDefinition.addPathDefinition("/css/**", "anon");
	        chainDefinition.addPathDefinition("/elementui/**", "anon");
	        chainDefinition.addPathDefinition("/js/**", "anon");
	        chainDefinition.addPathDefinition("/safty/login/**", "anon");
	        
	        
	        //加载动态权限 
	        List<Module> moduleList = shiroService.getAllSubModules();
	        
	        String PREMISSION_FORMAT = "authc,perms[{0}]";
	        
	        //动态权限设置
	        for(Module module:moduleList) {
	            
	        	if(StringUtils.isEmpty(module.getM_url())) {
	                continue;
	            }
	            
	            
	            chainDefinition.addPathDefinition(module.getM_url().replace("index.html", "**"), MessageFormat.format(PREMISSION_FORMAT, String.valueOf(module.getM_id())));
	        }
	        
	        //其它资源必须经过认证
	        chainDefinition.addPathDefinition("/**", "authc");
	        
	        LOG.debug("=====Shiro安全规则=======================================================================");
	        LOG.debug(chainDefinition.getFilterChainMap().toString());
	        LOG.debug("=====Shiro安全规则=======================================================================");
	        
	        return chainDefinition;
	    }

配置Shiro过滤器(starter方式下在application.properties中配置)

shiro:
  loginUrl: /loginTo
  successUrl: /successTo
  unauthorizedUrl: /unauthorizedTo

非starter实现springboot和Shiro的集成(比starter集成多配置Shiro安全管理器和Shiro生命周期管理器)

配置Shiro安全管理器(SecurityManager)
//配置Shiro安全管理器
	 	@Bean
	    @Autowired
	    public org.apache.shiro.mgt.SecurityManager shiroSecurityManager(Realm realm) {
	    	DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
	    	securityManager.setRealm(realm);
	    	return securityManager;
	    }
配置Shiro过滤器(非starter方式必须在Shiro配置文件中书写)
//配置shiro过滤器 (使用starter方式集成,可以直接在配置文件中配置此处)
	 @Bean
	    @Autowired
	    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager,ShiroFilterChainDefinition shiroFilterChainDefinition) throws Exception {
	    
	    	ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
	    	
	    	shiroFilterFactoryBean.setSecurityManager(securityManager);

			//设置登录url,shiro发现未认证时自动重定向到该url,如果是前后端分离式开发该url应当发送json串。
	    	shiroFilterFactoryBean.setLoginUrl("/loginTo");
	    	
	    	//设置未授权url,shiro发现未授权时自动重定向到该url,如果是前后端分离式开发该url应当发送json串。
	    	shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedTo");
	    	
	    	shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition.getFilterChainMap());
	    
	    	return shiroFilterFactoryBean;
	                     
	    }
配置Shiro生命周期管理器
@Bean
	    public org.apache.shiro.spring.LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
	    	return new org.apache.shiro.spring.LifecycleBeanPostProcessor();
	    }

使用Shiro

  1. 编译ShiroService和ShiroDao以及mapper,并且在Springboot项目中扫描ShiroDao所在位置(使用@MapperScan({“package 1”,“package 1”})或者在其dao层直接使用@Mapper注解)
  2. 登录(用户认证)
@PostMapping("/user")
	public Result login(@RequestBody UserDto userdto) {
		try {
			Subject subject = SecurityUtils.getSubject();
			//创建登录令牌
			UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userdto.getU_id(),userdto.getU_pwd());
			
			//登录,该方法声明抛出异常,可以捕获异常,进行处理
			subject.login(usernamePasswordToken);
			
			if(subject.isAuthenticated()) {
				return Result.success("登录成功!!!");
			}
			return Result.fail("登录失败!!");
		}catch(UnknownAccountException e) {
			return Result.fail("用户名不存在!");
		}catch (IncorrectCredentialsException e) {
	    	return Result.fail("账户密码不正确!");
	    } catch (LockedAccountException e) {
	    	return Result.fail("用户名被锁定 !");
	    }catch (Exception e) {
			e.printStackTrace();
			return Result.fail("系统升级中......");
		}
		
	}
  1. 安全退出
@DeleteMapping("/curruser")
	public Result loginOut() {
		SecurityUtils.getSubject().logout();
		return Result.success("成功退出!!!");
	}
  1. 修改密码
@PutMapping("/repair")
	public Result repairPwd(@RequestBody String[] pwd) {
		try {
			homeService.repairPwd(pwd,getCurrUser());
			SecurityUtils.getSubject().logout();
			return Result.success("成功修改!!!");
		}catch(BussnissException e) {
			return Result.fail(e.getMessage());
		}
	}
  1. 授权处理(这里叫放行应该更合适,符合授权信息对象中的数据,则放行,否则无权限)
@Service
@Transactional
@CacheConfig(cacheNames = "home")//统一指定缓存的名字
public class HomeServiceImpl implements HomeService{
	@Autowired
	private HomeDao homeDao;
	@Override
	@Cacheable(key="targetClass+'.'+methodName+'()'") //主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 
	public List<MenuDto> getMenu(CurrUser currUser) {
		正常获取菜单的代码
		这里的获取菜单:仅仅只是为了将菜单显示到前端页面;
			ShiroService的获取为了在用户访问菜单中的某一项内容是判断其有没有这个权限;
	}

注解说明

@Cacheable()
	//主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
	//value:缓存的名字 @Cacheable(value=”mycache”)  @Cacheable(value={”cache1”,”cache2”}
	//key:缓存的key,可以为空,如果指定需要按照SpEL表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @Cacheable(value=”testcache”,key=”#userName”)
		//SpEL表达式:Spring中的EL表达式
		//key中的EL表达式可以用:”#参数名“,”#p参数index“ @Cacheable(value="users", key="#id")    @Cacheable(value="users", key="#p0")
		//除了使用方法参数外,Spring还为我们提供了root对象,可以用来生成key。通过root可以获取一下参数:
		//methodName:当前方法名 #root.methodName
		//method:当前方法 #root.method
		//target:当前被调用的对象, 
		//targetClass:当前被调用对象的class
		//args:当前方法参数组成的数组 #root.args[0]
		//caches:当前被调用的方法使用的Cache #root.caches[0].name
		//使用root对象时,也可以将#root省略,因为spring默认使用的就是root对象的属性
	//condition:缓存的条件,可以为空(默认),使用SpEL表达式编写,返回true或者false。只有为True才能进行缓存
		//@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值