springboot 2.1.0集成jwt,shiro

自行添加 jwt与shiro依赖

1.jwt工具包

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import tech.liveeasy.biz.camphor.config.SystemConfigProperty;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

@Component
public class JWTUtil {


    @Autowired
    private SystemConfigProperty systemConfigProperty;

    /**
     * 获取登陆名
     *
     * @param token
     * @return
     */
    public String getLoginName(String token) {
        Claims claims = Jwts
                .parser()
                .setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
                .parseClaimsJws(token)
                .getBody();
        return claims.getSubject();
    }

    /**
     * 获取token参数
     *
     * @param token
     * @param key
     * @return
     */
    public String getclaimsValue(String token, String key) {
        Claims claims = Jwts
                .parser()
                .setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
                .parseClaimsJws(token)
                .getBody();
        return claims.get(key, String.class);
    }


    /**
     * 获取token参数
     *
     * @param token
     * @param key
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getclaimsValue(String token, String key, Class<T> clazz) {
        Claims claims = Jwts
                .parser()
                .setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
                .parseClaimsJws(token)
                .getBody();
        return claims.get(key, clazz);
    }

    /**
     * 验证token是否正确
     *
     * @param token
     * @param username
     * @param secret
     * @return
     */
    public boolean verify(String token, String username, String secret) {
        Claims claims = Jwts
                .parser()
                .setSigningKey(systemConfigProperty.getJwtAppKey())//自定义key
                .parseClaimsJws(token)
                .getBody();
        String subject = claims.getSubject();
        String secret1 = claims.get("secret", String.class);
        return username.equals(subject) && secret1.equals(secret);
    }

    /**
     * 创建toKen
     *
     * @param username
     * @param password
     * @return
     */
    public String createToken(String username, String password, Map<String, Object> parameters) {
        Calendar curr = Calendar.getInstance();
        curr.set(Calendar.DAY_OF_YEAR, curr.get(Calendar.DAY_OF_YEAR) + systemConfigProperty.getJwtAppTokenTimeout());//自定义有效天数

        if (null == parameters) {
            parameters = new HashMap<>(16);
        }
        parameters.put("sub", username);
        parameters.put("secret", password);

        JwtBuilder jwtBuilder = Jwts
                .builder()
                .setSubject(username)
                .setClaims(parameters)
                .setExpiration(curr.getTime())
                .signWith(SignatureAlgorithm.HS256, systemConfigProperty.getJwtAppKey());//自定义key

        return jwtBuilder.compact();
    }

    /**
     * 创建toKen
     *
     * @param username
     * @param password
     * @return
     */
    public String createToken(String username, String password) {
        return createToken(username, password, null);
    }
}


2.shiro token修改


import org.apache.shiro.authc.AuthenticationToken;

public class SystemToken implements AuthenticationToken {

    private String token;

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

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

    @Override
    public Object getCredentials() {
        return token;
    }
}

3.自定义异常

import org.apache.shiro.authc.AuthenticationException;

public class UnauthorizedException extends AuthenticationException {

    private int errorCode;

    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }

    public UnauthorizedException(int errorCode , String msg ) {
        super(msg);
        this.errorCode = errorCode;
    }

    public UnauthorizedException(String msg) {
        super(msg);
    }

    public UnauthorizedException() {
        super();
    }
}

4.自定义SystemRealm

import io.jsonwebtoken.ExpiredJwtException;
import org.apache.commons.lang3.StringUtils;
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 tech.liveeasy.biz.camphor.apps.user.entity.UserEntity;
import tech.liveeasy.biz.camphor.system.excption.ExceptionCode;
import tech.liveeasy.biz.camphor.system.excption.UnauthorizedException;
import tech.liveeasy.biz.camphor.system.utils.JWTUtil;
import tech.liveeasy.biz.camphor.system.utils.UserUtil;

import java.util.HashSet;
import java.util.Set;


@Component
public class SystemRealm extends AuthorizingRealm {

    @Autowired
    private JWTUtil jwtUtil;

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

    /**
     * //角色权限和对应权限添加 通过subject.isPermited()等方法调用
     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = jwtUtil.getLoginName(principals.toString());
        UserEntity user = UserUtil.getUserByLoginName(username);
        Set<String> set = new HashSet<>();
        set.add(user.getRules());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setRoles(set);
        simpleAuthorizationInfo.addStringPermissions(user.getPermissionList());
        return simpleAuthorizationInfo;
    }

    /**
     * //用户认证 通过subject.login()等方法来调用
     * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        String token = (String) auth.getCredentials();
        String username = "";
        try {
            username = jwtUtil.getLoginName(token);
        } catch (ExpiredJwtException expiredJwtException) {
            throw new UnauthorizedException(ExceptionCode.TOKEN_EXPIRED.getCode(), ExceptionCode.TOKEN_EXPIRED.getName());
        }
        if (StringUtils.isEmpty(username)) {
            throw new UnauthorizedException(ExceptionCode.UNKNOWN_ACCOUNT.getCode(), ExceptionCode.UNKNOWN_ACCOUNT.getName());
        }
        String password = UserUtil.getPassword(username);
        String status = UserUtil.getStatus(username);
        if (!jwtUtil.verify(token, username, password)) {
            throw new UnauthorizedException(ExceptionCode.AUTHENTICATION.getCode(), ExceptionCode.AUTHENTICATION.getName());
        }
        if (!"在职".equals(status)) {
            throw new UnauthorizedException(ExceptionCode.DISABLED_ACCOUNT.getCode(), ExceptionCode.DISABLED_ACCOUNT.getName());
        }
        return new SimpleAuthenticationInfo(token, token, "system_realm");
    }
}

5.设置filter


import com.alibaba.fastjson.JSON;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import tech.liveeasy.biz.camphor.apps.common.RestfulResult;
import tech.liveeasy.biz.camphor.system.excption.ExceptionCode;
import tech.liveeasy.biz.camphor.system.excption.UnauthorizedException;
import tech.liveeasy.biz.camphor.system.security.SystemToken;

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

public class SystemFilter extends BasicHttpAuthenticationFilter {
    private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    private String rootPath;

    public SystemFilter(String rootPath) {
        this.rootPath = rootPath;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            setHeader(httpServletRequest, httpServletResponse);
            return false;
        }
        return super.preHandle(request, response);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        if (isLoginAttempt(request, response)) {
            try {
                executeLogin(request, response);
                return true;
            } catch (Exception e) {
                throw (RuntimeException) e;
            }
        } else {
            //如果请求头不存在 Authorization,则可能是执行不合规操作直接返回 false 进入onAccessDenied
            return false;
        }
    }

    /**
     * 处理未经验证的请求
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        throw new UnauthorizedException(ExceptionCode.BLANK_TOKEN.getCode(), ExceptionCode.BLANK_TOKEN.getName());
       /* boolean loggedIn = false;
        if (this.isLoginAttempt(request, response)) {
            loggedIn = this.executeLogin(request, response);
        }

        if (!loggedIn) {
            this.sendChallenge(request, response);
        }

        return loggedIn;*/
    }

    /**
     * 构建未授权的请求返回,filter层的异常不受exceptionAdvice控制,这里返回401,把返回的json丢到response中
     */
    /*@Override
    protected boolean sendChallenge(ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        String contentType = "application/json;charset=UTF-8";
        httpResponse.setStatus(401);
        httpResponse.setContentType(contentType);
        try {
            String msg = "对不起,您无权限进行操作!";
            RestfulResult unauthentication = new RestfulResult();
            unauthentication.setCode("401");
            unauthentication.setMsg(msg);
            PrintWriter printWriter = httpResponse.getWriter();
            printWriter.append(JSON.toJSONString(unauthentication));
        } catch (IOException e) {
            LOGGER.error("sendChallenge error,can not resolve httpServletResponse");
        }

        return false;
    }*/

    /**
     * 判断用户是否想要登入
     * 检测header里面是否包含Authorization字段即可
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String authorization = req.getHeader("Authorization");
        return authorization != null && !authorization.equals("");
    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = httpServletRequest.getHeader("Authorization");
        SystemToken token = new SystemToken(authorization);
        getSubject(request, response).login(token);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    @Override
    protected void cleanup(ServletRequest request, ServletResponse response, Exception existing) throws ServletException, IOException {
        if (null == existing) {
            super.cleanup(request, response, existing);
        } else {
            response401(response, existing);
        }
    }

    private void response401(ServletResponse response, Exception throwable) {
        try {
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            String url = rootPath + "/user";
            if (throwable instanceof UnauthorizedException) {
                UnauthorizedException exception = (UnauthorizedException) throwable;
                url += "/401?errorCode=" + exception.getErrorCode() + "&msg=" + exception.getMessage();
            } else {
                url += "/401";
            }
            httpServletResponse.sendRedirect(url);
        } catch (IOException e) {
            LOGGER.error("response401:", e);
        }
    }

    @Override
    protected boolean isRememberMe(ServletRequest request) {
        return super.isRememberMe(request);
    }

    /**
     * 为response设置header,实现跨域
     */
    private void setHeader(HttpServletRequest request, HttpServletResponse response) {
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
        response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        //防止乱码,适用于传输JSON数据
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }
}

最后配置shiro

import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.StringRedisTemplate;
import tech.liveeasy.biz.camphor.system.filter.CaptchaValidateFilter;
import tech.liveeasy.biz.camphor.system.filter.PhoneCaptchaFilter;
import tech.liveeasy.biz.camphor.system.filter.SystemFilter;
import tech.liveeasy.biz.camphor.system.filter.SystemLogoutFilter;
import tech.liveeasy.biz.camphor.system.security.SystemRealm;
import tech.liveeasy.platform.common.config.Global;

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

@Configuration
public class ShiroConfiguration {

    @Bean("securityManager")
    public DefaultWebSecurityManager getManager(SystemRealm systemRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(systemRealm);
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        manager.setSubjectDAO(subjectDAO);
        return manager;
    }

    @Bean("shiroFilter")
    public ShiroFilterFactoryBean factory(DefaultWebSecurityManager securityManager,
                                          SystemConfigProperty systemConfigProperty) {

        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

        // 添加自定义规则的过滤器
        Map<String, Filter> filterMap = new HashMap<>();
        //接口权限验证
        filterMap.put("system", new SystemFilter(systemConfigProperty.getRootPath()));
        factoryBean.setFilters(filterMap);
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setUnauthorizedUrl(systemConfigProperty.getRootPath() + "/401");
       
        Map<String, String> filterRuleMap = new HashMap<>();
        filterRuleMap.put("/401", "anon");
        filterRuleMap.put("/user/login", "anon");
        filterRuleMap.put("/**", "system");

        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    /**
     * 下面的代码是添加注解支持
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制使用cglib,防止重复代理和可能引起代理出错的问题
        // https://zhuanlan.zhihu.com/p/29161098
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

发布了97 篇原创文章 · 获赞 44 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/wangh92/article/details/89154909