springboot shiro

本文档展示了如何在Spring项目中配置Apache Shiro进行权限管理。包括引入Shiro-Spring依赖,配置ShiroFilterFactoryBean,自定义 Realm 进行认证和授权,以及创建自定义过滤器Auth2Filter实现登录验证。通过这种方式,实现了基于token的权限控制,支持用户登录、权限校验和异常处理等功能。

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

一 pom依赖

		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>

二 Shiro配置

package com.kong.qyk8.config;

import com.kong.qyk8.modules.sys.auth2.Auth2Filter;
import com.kong.qyk8.modules.sys.auth2.Auth2Realm;
import org.apache.shiro.mgt.SecurityManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro配置
 */
@Configuration
public class ShiroConfig {

    /**
     * 将自己的验证方式加入容器
     */
    @Bean
    public Auth2Realm myAuth2Realm() {
        return new Auth2Realm();
    }

    /**
     * 注入安全管理器
     * 权限管理,配置主要是Realm的管理认证
     *
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myAuth2Realm());
        return securityManager;
    }

    /**
     * url拦截
     *
     * @param securityManager 管理认证
     * @return ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        //auth过滤
        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("auth2", new Auth2Filter());
        shiroFilter.setFilters(filters);
        /*authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问*/
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/druid/**", "anon");
        filterMap.put("/app/**", "anon");
        filterMap.put("/sys/login", "anon");
        filterMap.put("/swagger**/**", "anon");
        filterMap.put("/v2/api-docs**", "anon");
        filterMap.put("/swagger-ui.html**", "anon");
        filterMap.put("/swagger-resources**/**", "anon");
        filterMap.put("/captcha.jpg", "anon");
        filterMap.put("**/**", "auth2");

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }
    /**
     * 加入注解的使用,不加入这个注解不生效
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

三 自定义Realm

package com.kong.qyk8.modules.sys.auth2;

import com.kong.qyk8.common.utils.DateUtils;
import com.kong.qyk8.modules.sys.entity.SysUserEntity;
import com.kong.qyk8.modules.sys.entity.SysUserTokenEntity;
import com.kong.qyk8.modules.sys.service.ShiroService;
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.Set;

/**
 * 自定义认证
 *
 * @author kong
 * @email iskong88@163.com
 * @date 2021-06-12 21:39:35
 */
@Component
public class Auth2Realm extends AuthorizingRealm {

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

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUserEntity user = (SysUserEntity) principals.getPrimaryPrincipal();
        //用户权限列表
        Set<String> permsSet = 自己去查询就得了,简单的抱头;
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permsSet);
        return info;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();
        //TODO 根据accessToken,查询用户信息
        SysUserTokenEntity tokenEntity = shiroService.queryByToken(accessToken);
        //token失效
        if (tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()) {
            throw new IncorrectCredentialsException("token失效,请重新登录");
        }

        //TODO 查询用户信息
        SysUserEntity user = shiroService.queryUser(tokenEntity.getUserId());
        //TODO 账号锁定
        if (user.getStatus() == 0) {
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }

        return new SimpleAuthenticationInfo(user, accessToken, getName());
    }
}

四 自定义过滤器Auth2Filter

package com.kong.qyk8.modules.sys.auth2;

import com.google.gson.Gson;
import com.kong.qyk8.common.utils.HttpContextUtils;
import com.kong.qyk8.common.utils.R;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * auth2过滤器
 *
 * @author kong
 * @email iskong88@163.com
 * @date 2021-06-12 21:39:35
 */
public class Auth2Filter extends AuthenticatingFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        System.out.println("auth2过滤器createToken");
        //获取请求token
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            return null;
        }
        return new Auth2Token(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        System.out.println("auth2过滤器 isAccessAllowed");
        if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("auth2过滤器 onAccessDenied");
        //获取请求token,如果token不存在,直接返回401
        String token = getRequestToken((HttpServletRequest) request);
        if (StringUtils.isBlank(token)) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
            httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());

            String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));

            httpResponse.getWriter().print(json);

            return false;
        }
        return executeLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        System.out.println("auth2过滤器 onLoginFailure");
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());
        try {
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
            R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

            String json = new Gson().toJson(r);
            httpResponse.getWriter().print(json);
        } catch (IOException e1) {
        }
        return false;
    }

    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest) {
        //从header中获取token
        String token = httpRequest.getHeader("token");
        System.out.println("auth2过滤器-----从header中获取token= " + token);
        //如果header中不存在token,则从参数中获取token
        if (StringUtils.isBlank(token)) {
            token = httpRequest.getParameter("token");
        }
        return token;
    }
}

五 Auth2Token

package com.kong.qyk8.modules.sys.auth2;


import org.apache.shiro.authc.AuthenticationToken;

/**
 * token
 *
 * @author kong
 * @email iskong88@163.com
 * @date 2021-06-12 21:39:35
 */
public class Auth2Token implements AuthenticationToken {
    private String token;

    public Auth2Token() {
    }

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

    /**
     * 返回在身份验证过程中提交的帐户标识
     *
     * @return token
     */
    @Override
    public String getPrincipal() {
        return token;
    }

    /**
     * 返回用户在验证的身份验证过程中提交的凭据
     * 提交的{getPrincipal() 帐户标识}。
     *
     * @return token
     */
    @Override
    public Object getCredentials() {
        return token;
    }
}

六 登录接口

@Override
    public Map<String, Object> login(SysLoginForm form) {
        //获取图片验证码sysCaptchaService
        boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());
        if (!captcha) {
            return R.error("验证码不正确");
        }

        //用户信息
        SysUserEntity user = sysUserService.queryByUserName(form.getUsername());
        //账号不存在、密码错误
        if (user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {
            return R.error("账号或密码不正确");
        }

        //账号锁定
        if (user.getStatus() == 0) {
            return R.error("账号已被锁定,请联系管理员");
        }
        /*
         * 获取用户权限,给前端使用
         */
        Set<String> permissions = shiroService.getUserPermissions(user.getUserId());
        /*
         * 生成token,并保存到数据库
         */
        R token = sysUserTokenService.createToken(user.getUserId()).put("permissions", permissions);
        /*
         * shiro 认证,授权 token
         */
        Subject subject = SecurityUtils.getSubject();
        subject.login(new Auth2Token(token.get("token").toString()));
        //
        return token;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值