shiro

用来做登录认证和权限管理,跟拦截器类似,可以设置未登录状态去到某个页面,登录后去到某个页面,可以设置权限标识,有权限标识的才能使用某些功能。

使用spring整合shiro来显示不同用户看到不同的菜单,以及不同权限拥有不同的操作

  1. 添加shiro依赖
<!-- shiro spring. -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro core -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
  1. 在resources文件夹下新建一个spring文件夹下新建一个shiro.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">


    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- ref对应我们写的realm myRealm -->
        <property name="realm" ref="AuthRealm" />
        <!-- 使用下面配置的缓存管理器 -->
        <!-- <property name="cacheManager" ref="shiroEncacheManager" /> -->
    </bean>

    <!-- 安全认证过滤器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 调用我们配置的权限管理器 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 配置我们的登录请求地址 -->
        <property name="loginUrl" value="/toLogin" />
        <!-- 配置我们在登录页登录成功后的跳转地址,如果你访问的是非/login地址,则跳到您访问的地址 -->
        <property name="successUrl" value="/toIndex" />
        <!-- 如果您请求的资源不再您的权限范围,则跳转到错误请求地址 -->
        <property name="unauthorizedUrl" value="/toError" />
        <!--anon都可以访问-->
        <!--authc授权了后才可以访问-->
        <property name="filterChainDefinitions">
            <value>
                /statics/**=anon
                /login=anon
                /** = authc
            </value>
        </property>
    </bean>
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

    <!-- AOP式方法级权限检查 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
</beans>
  1. 新建一个AuthRealm类继承AuthorizingRealm并重写方法,并交给spring容器管理
package com.ossjk.config.shiro;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ossjk.core.constant.Constant;
import com.ossjk.hui.system.entity.Permission;
import com.ossjk.hui.system.entity.User;
import com.ossjk.hui.system.service.IPermissionService;
import com.ossjk.hui.system.service.IUserService;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Component("AuthRealm")
public class AuthRealm extends AuthorizingRealm {
    @Autowired
    private IUserService iUserService;
    @Autowired
    private IPermissionService iPermissionService;
    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取授权信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        System.out.println("000000000000000000000000000000000000000000");
        List<String> urls = (List<String>) SecurityUtils.getSubject().getSession().getAttribute(Constant.SESSION_URLS);
        //授权权限
        simpleAuthorizationInfo.addStringPermissions(urls);

        System.out.println("=====================");
        for (String url:urls) {
            System.out.println(url);
        }
        return simpleAuthorizationInfo;
    }

    /**
     * 验证登陆
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //验证登陆 UsernamePasswordToken token = new UsernamePasswordToken(name,pwd);拿到构建登录时传来的账号密码
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        //拿到的是一个字符数组,可便于加密操作
        char[] password = usernamePasswordToken.getPassword();
        //这里就不进行加密了直接转为字符串
        String pwd = new String(password);
        //调用service层查询用户
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("name", username);
        User user = iUserService.getOne(userQueryWrapper);
        //判断用户是否存在
        if(ObjectUtil.isNull(user)){
            //用户不存在,抛异常
            throw new UnknownAccountException();
        }
        //判断密码是否正确
        if(!ObjectUtil.equal(user.getPwd(),pwd)){
            //密码不正确,抛异常
            throw new IncorrectCredentialsException();
        }
        //验证成功
        //shiro获取session并将用户放到session中
        SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_USER,user);
        //调用service层,查询用户的所有权限
        List<Permission> permissions = iPermissionService.listByUid(user.getId());
        //从所有权限中过滤出用户所拥有的菜单权限
        List<Permission> menus = permissions.stream().filter(t -> {
            return (t.getLevel().intValue() == 0) || (t.getLevel().intValue() == 1);
        }).collect(Collectors.toList());
        //将菜单放到session
        SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_MENUS,menus);
        //从权限信息中过滤出用户所拥有的操作权限
        List<String> urls = permissions.stream().filter(t -> {
            return (t.getLevel().intValue() == 1 || t.getLevel().intValue() == 2);
        }).map(t -> {
            return t.getUrl();
        }).collect(Collectors.toList());
        //将用户操作权限放到session
        SecurityUtils.getSubject().getSession().setAttribute(Constant.SESSION_URLS,urls);

        //构造认证信息
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPwd(),getName());

        return info;
    }
}
  1. 在登陆方法内调用shiro认证登陆
/**
	 * 登录
	 *
	 * @param name
	 * @param pwd
	 * @return
	 */
	@RequestMapping("/login")
	public String login(String name, String pwd, ModelMap map, HttpSession session) {
		//构造shiro需要的登陆参数
		UsernamePasswordToken token = new UsernamePasswordToken(name,pwd);
		try{
			//交给shiro去验证登陆
			SecurityUtils.getSubject().login(token);
		}catch (UnknownAccountException uae){
			map.put("msg","未知用户");
			return "login";
		}catch (IncorrectCredentialsException ice){
			map.put("msg","密码错误");
			return "login";
		}catch (AuthenticationException ae){
			map.put("msg","服务器忙");
			return "login";
		}
		//验证成功去到首页
		return "redirect:/toIndex";

	}
  1. 在Controller上添加权限判断的注解
/**
	 * 列表
	 */
	//判断有没有该权限,有的话可处理请求,没有的话会报错
	@RequiresPermissions("/system/user/list")
	@RequestMapping("/list")
	public String list(String name, String phone, Page page, ModelMap map) {
		QueryWrapper<User> queryWrapper = new QueryWrapper();
		if (StrUtil.isNotBlank(name)) {
			queryWrapper.like("u.name", name).or().like("phone", name);
		}
		if (StrUtil.isNotBlank(phone)) {
			queryWrapper.like("phone", phone);
		}
		map.put("name", name);
		map.put("phone", phone);
		map.put("page", new MyPage(iUserService.pageVo(page, queryWrapper)));
		return "system/user/list";
	}
  1. 在前端页面引入shiro标签库并使用shiro标签判断权限,有权限的才能看到标签内的内容
<%@ taglib prefix="" uri="http://shiro.apache.org/tags" %>
<!-- name属性进行指定的权限判断,有权限才会显示标签内的内容 -->
<shiro:hasPermission name="/system/user/toInsert">
                                <a href="javascript:;"
                                   onclick="insert('添加','${pageContext.request.contextPath}/system/user/toInsert','800','500')"
                                   class="btn btn-primary radius"> <i class="Hui-iconfont"></i> 添加
                                </a>
                            </shiro:hasPermission>
  1. 使用shiro进行退出登陆
/**
	 * 注销
	 *
	 * @return
	 */
	@RequestMapping("/logout")
	public String logout(HttpSession session) {
		//使用shiro退出登录
		SecurityUtils.getSubject().logout();
		session.removeAttribute(Constant.SESSION_USER);
		return "redirect:/";
	}