SpringDataJpa07-Shiro权限与菜单

1.Shiro权限框架的使用

1.1、FilterChainDefinitionMapFactory

								自定义拦截器
								web/shiro
public class FilterChainDefinitionMapFactory {
    @Autowired
    private IPermissionService permissionService;
    public Map<String, String> createMap() {
        Map<String, String> map = new LinkedHashMap<>();
        //一定注意顺序
        //不拦截的
        map.put("/login","anon");
        //把所有的静态资源(js,css,图片,...)放行
        map.put("*.js","anon");
        map.put("*.css","anon");
        map.put("/css/**","anon");
        map.put("/js/**","anon");
        map.put("/easyui/**","anon");
        map.put("/images/**","anon");
        //添加权限拦截数据,从数据库中获取
        List<Permission> permissions = permissionService.findAll();
        permissions.forEach(permission -> {
            map.put(permission.getUrl(),"perms["+permission.getSn()+"]");
        });
        //拦截所有
        map.put("/**","authc");
        return map;
    }
}

1.2、PermissionRepository

								查询用户权限
								repository
/**
 * 第一个泛型:代表类型
 * 第二个泛型:主键类型
 * 返回的是Set<String>,与AisellRealm对应
 */
public interface PermissionRepository extends BaseRepository<Permission,Long> {

    @Query("select p.sn from Employee e join e.roles r join r.permissions p where e.id=?1")
    Set<String> findPermission(Long userId);
}

1.3AisellRealm

							自定义realm授权与登录
							web/shiro
public class AisellRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    /**
     * 授权
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //创建一个授权对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //拿到与设置权限
        Set<String> perms = permissionService.findPermission();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    /**
     * 登录功能就写在这里面
     *      1.如果这里返回null,就代表用户名错误
     *      2.我们只需要把密码交给shiro,它会自动帮我们判断
     */
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.拿到用户和密码(令牌)
        // 1.1 转成用户名密码令牌
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        // 1.2 获取用户名
        String username = token.getUsername();
        //2.判断是否存在
        Employee employee = employeeService.findByUsername(username);
        if(employee==null){
            //如果用户为null,代表你的用户名没有查到数据=>这个用户是不存在的
            return null;
        }
        //3.判断密码是否正确
        /**
         * 第一个参数:principal(主体) =》 登录成功后在任何地方都可以拿到的对象
         *      以前开始登录成功我们就把用户放到session中
         * 第二个参数:密码(从数据库查出来的密码)
         * 第三个参数:盐值
         * 第四个参数:当前realm的名称(随便取)
         */
        //shiro做准备了一个工具直接让我们把数据变成 ByteSource格式
        ByteSource salt = ByteSource.Util.bytes("itsource");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());
        return authenticationInfo;
    }

}

1.4 自定义权限

				写一个类继承 PermissionsAuthorizationFilter
				web/shiro
public class AiSellPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        //没有权限时 执行的规则
        Subject subject = this.getSubject(request, response);
        if (subject.getPrincipal() == null) {
            this.saveRequestAndRedirectToLogin(request, response);
        } else {
            //1.转换成Http协议对象
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            //设置响应头为json 让浏览器解析
            resp.setContentType("application/json;charset=UTF-8");
            //2.获取请求头: XMLHttpRequest
            String xhr = req.getHeader("X-Requested-With");
            if("XMLHttpRequest".equals(xhr)){
                //3.代表现在是Ajax请求,我们得做ajax处理
                // 返回一个json:{success:false,msg:xxx}
                resp.getWriter().print("{\"success\":false,\"msg\":\"没有权限\"}");
            }else{
                //普通请求,和以前一样处理即可
                //拿到没有权限的页面
                String unauthorizedUrl = this.getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    //如果有路径,就会跳到不有权限的位置
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    //如果没有路径,就会报401的错误
                    WebUtils.toHttp(response).sendError(401);
                }
            }
        }
        return false;
    }
}

1.5 applicationContext-shiro.xml配置拦截器

注意名字要和自定义拦截器的名字相同 filterChainDefinitionMap

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!--创建一个权限管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="aisellRealm"/>
    </bean>
    <!--创建相应的Realm-->
    <bean id="aisellRealm" class="cn.itsource.aisell.web.shiro.AisellRealm">
        <!--名称-->
        <property name="name" value="aisellRealm"/>
        <!--凭证匹配器 -->
        <property name="credentialsMatcher">
            <!-- 创建hash匹配器-->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--加密算法-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--迭代次数-->
                <property name="hashIterations" value="10" />
            </bean>
        </property>
    </bean>

    <!-- 建议大家留住它:可以支持注解权限控制 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!--shiro真实的权限过滤器(这个名字必需和shiro过滤器的名字一致)-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 引入了权限管理器 -->
        <property name="securityManager" ref="securityManager"/>
        <!-- 没有登录进入的路径 -->
        <property name="loginUrl" value="/login"/>
        <!-- 成功登录会进入的页面 -->
        <property name="successUrl" value="/main"/>
        <!--没有权限进入的页面-->
        <property name="unauthorizedUrl" value="../index.jsp"/>
        <!--
            filterChainDefinitions:过滤器描述(它是有顺序)
                anon:不登录也可以访问(游客可以访问的页面)
                authc:必需登录才可以访问
          -->
        <!--
        <property name="filterChainDefinitions">
            <value>
                /login = anon
                /s/permission.jsp = perms[employee:index]
                /department/index = perms[department:index]
                /两个星号 = authc
            </value>
        </property>
        -->

        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
        <property name="filters">
            <map>
                <!--引用自己得过滤器-->
                <entry key="aisellPerms" value-ref="aisellFilter" />
            </map>
        </property>
    </bean>
    <!--配置咱们自己的过滤器-->
    <bean id="aisellFilter" class="cn.itsource.aisell.web.shiro.AiSellPermissionsAuthorizationFilter" />

    <!-- 把过滤器描述放在java代码中方便修改 -->
    <!--把工厂bean中的createMap方法返回的对象 也变成一个bean-->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="createMap" />
    <!--创建工厂bean-->
    <bean id="filterChainDefinitionMapFactory" class="cn.itsource.aisell.web.shiro.FilterChainDefinitionMapFactory" />

</beans>

2.菜单显示

流程 用户->角色->权限->菜单 只显示有权限的菜单

public interface MenuRepository extends BaseRepository<Menu,Long>{
    //根据用户名拿到一个人对应的所有子菜单
    @Query("select distinct m from Employee e join e.roles r join r.permissions p join p.menu m where e.id=?1")
    List<Menu> findMenusByUser(Long userId);
}

父菜单可以找到子菜单 子菜单也可以找到父菜单 就会无限循环
@JsonIgnore:生成JSON的时候忽略这个属性

/*
    * 多对一,儿子菜单,可以找到父亲菜单
    *JsonIgnore:生成JSON的时候忽略这个属性
    * */
    @ManyToOne
    @JoinColumn(name = "parent_id")
    @JsonIgnore
    private Menu parent;

    /*@OneToMany
    @JoinColumn(name = "parent_id")*/
    /*
    * 临时属性
    *       这里不能配一对多
    *       在一对多的情况下,菜单会通过父级菜单把所有子级菜单展示
    *       达不到权限的目的。
    *
    * */
    @Transient
    private List<Menu> children = new ArrayList<>();
@Service
public class MenuServiceImpl extends BaseServiceImpl<Menu, Long> implements IMenuService {
    @Autowired
    private MenuRepository menuRepository;

    @Override
    public List<Menu> findParentMenu() {
        //创建一个父菜单容器
        List<Menu> parentMenus = new ArrayList<>();
        //拿到当前用用户的菜单
        Employee user = UserContext.getUser();
        List<Menu> menus = menuRepository.findMenusByUser(user.getId());
        //遍历子菜单
        menus.forEach(menu -> {
            Menu menuParent = menu.getParent();
            //判断是否已经有了
            if(!parentMenus.contains(menuParent)){
                parentMenus.add(menuParent);
            }
            menuParent.getChildren().add(menu);
        });
        return parentMenus;
    }

拿到当前用户

/*
* 拿到当前用户
* */
public static Employee getUser(){
    //当前用户
    Subject subject = SecurityUtils.getSubject();
    //拿到主体(当前登录用户,这里我们写的就是employee,所以强转)
    Employee loginUser = (Employee) subject.getPrincipal();
    return loginUser;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值