springboot集成shiro前后端分离权限控制

本文档展示了如何在前后端分离的环境中使用Shiro进行权限管理,包括配置ShiroFilter、自定义Realm实现用户认证与授权,以及自定义权限过滤器。通过用户-角色-资源关联关系,限制后台访问权限,并提供登录、登出、权限校验等核心功能。

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

后台权限管理控制需基于用户-角色-资源关联关系限制,本文仅演示前后端分离情况下使用shiro作为权限管理工具

shiro配置文件

@Configuration
public class ShiroConfig {

    @Bean("securityManager")
    public SessionsSecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }


    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //添加自定义的认证授权过滤器
        Map<String, Filter> filters = new HashMap<>();
        filters.put("auth", new AuthFilter());
        shiroFilter.setFilters(filters);

        //添加拦截路径
        Map<String, String> filterMap = new LinkedHashMap<String, String>();

        filterMap.put("/swagger/**", "anon");
        filterMap.put("/v2/api-docs", "anon");
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/webjars/**", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/statics/**", "anon");
        filterMap.put("/login.html", "anon");
        filterMap.put("/favicon.ico", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/sys/logout", "anon");
        //除以上所有请求之外 均被自定义权限过滤拦截校验
        filterMap.put("/**", "auth");

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

    /**
     * 配置以下bean借助SpringAOP扫描使用Shiro注解的类
    *
    * */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

自定义realm认证


@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private RedisUtils redisUtils;

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof AuthToken;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
        //获取前端传来的token
        String accessToken = (String) authToken.getPrincipal();
        String username = (String) redisUtils.get(RedisKey.SYS_USER+accessToken);
        if (username == null)
            throw new IncorrectCredentialsException("token失效,请重新登录");
        SysUser user = sysUserService.findByUsername(username);
        if (user == null)
            throw new UnknownAccountException("用户不存在!");
        if (user.getStatus() == 0)
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        //传入user以及获取到的token实现自动认证
        return new SimpleAuthenticationInfo(user, accessToken, getName());
    }

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user = (SysUser) principals.getPrimaryPrincipal();
        Long userId = user.getUserId();
        //获取用户权限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(getUserPermissions(userId));
        return info;
    }

    //获取用户权限
    private Set<String> getUserPermissions(long userId) {
        List<String> permsList;
        //管理员权限列表
        if (userId == CommonConstant.SUPER_ADMIN) {
            List<SysMenu> menuList = sysMenuService.selectAll();
            permsList = new ArrayList<>(menuList.size());
            for (SysMenu menu : menuList) {
                permsList.add(menu.getPerms());
            }
        } else {
            permsList = sysUserService.queryAllPerms(userId);
        }

        //用户权限列表
        Set<String> permsSet = new HashSet<>();
        for (String perms : permsList) {
            if (StringUtils.isBlank(perms))
                continue;
            permsSet.addAll(Arrays.asList(perms.trim().split(",")));
        }
        return permsSet;
    }
}
自定义权限过滤器
public class AuthFilter extends AuthenticatingFilter {

    Gson gson = new Gson();

    //生成自定义token
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        String token = getUserToken((HttpServletRequest) request);
        return new AuthToken(token);
    }


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        return ((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name());
    }


    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String token = getUserToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpServletResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setCharacterEncoding("UTF-8");
            httpServletResponse.getWriter().write(gson.toJson(ResponseResult.error("请先登录")));
            return false;
        }
        return executeLogin(request, response);
    }


    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpResponse.setCharacterEncoding("UTF-8");
        try {
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            httpResponse.getWriter().write(gson.toJson(gson.toJson(ResponseResult.error("登录凭证已失效,请重新登录"))));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return false;
    }

    //如果header中不存在token,则从参数中获取token
    private String getUserToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        if (StringUtils.isBlank(token))
            token = request.getParameter("token");
        return token;
    }
}
生成认证token
public class AuthToken implements AuthenticationToken {
    private String token;

    public AuthToken(String token) {
        this.token = token;
    }

    @Override
    public String getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}
token生成工具类
public class TokenGenerator {

    public static String generateValue() {
        return generateValue(UUID.randomUUID().toString());
    }

    private static final char[] hexCode = "0123456789abcdef".toCharArray();

    public static String toHexString(byte[] data) {
        if(data == null) {
            return null;
        }
        StringBuilder r = new StringBuilder(data.length*2);
        for ( byte b : data) {
            r.append(hexCode[(b >> 4) & 0xF]);
            r.append(hexCode[(b & 0xF)]);
        }
        return r.toString();
    }

    public static String generateValue(String param) {
        try {
            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(param.getBytes());
            byte[] messageDigest = algorithm.digest();
            return toHexString(messageDigest);
        } catch (Exception e) {
            throw new ApiException("生成Token失败", e);
        }
    }
}
测试接口示例
//测试账户需先建立 角色-用户-资源 关联关系 该示例中用户拥有sys:menu:info权限
@PostMapping(value = "/login")
    @ApiOperation(value = "登录", notes = "登录")
    public Result login(@RequestBody LoginForm param) {
        SysUser user = sysUserService.findByUsername(param.getUsername());
        String password = param.getPassword();
    
        if (user == null) {
            return Result.error("账号错误");
        } 
        if (!password.equals(user.getPassword())) {
            return Result.error("密码错误");
        } 
        String token = TokenGenerator.generateValue();
        redisUtils.set(RedisKey.SYS_USER + token, param.getUsername(), 3600 * 8);
            return Result.success(token);
        }
    }


    @GetMapping(value = "/logout")
    @ApiOperation(value = "退出", notes = "退出")
    public String logout(@RequestParam("token") String token) {
        redisUtils.del(RedisKey.SYS_USER + token);
        return "redirect:login.html";
    }

    @GetMapping("/info")
    @RequiresPermissions("sys:menu:info")
    @ApiOperation(value = "测试用菜单信息", notes = "测试用菜单信息")
    public Result info() {
        return Result.success("恭喜你成功了··");
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值