springboot篇】十六. springboot整合shiro和swagger2实现前后分离

springboot整合shiro和swagger2实现前后分离

中国加油,武汉加油!

篇幅较长,配合右边目录观看

项目准备

  1. 创建springboot项目nz1904-springboot-08-shiro-swagger
  2. 加入Web的SpringWeb依赖和Lombox依赖
  3. 导相关依赖
    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>fastjson</artifactId>
    	<version>1.2.54</version>
    </dependency>
    <!--swagger2相关依赖-->
    <dependency>
    	<groupId>io.springfox</groupId>
    	<artifactId>springfox-swagger2</artifactId>
    	<version>2.7.0</version>
    </dependency>
    <dependency>
    	<groupId>io.springfox</groupId>
    	<artifactId>springfox-swagger-ui</artifactId>
    	<version>2.7.0</version>
    </dependency>
    <!-- spring跟shiro整合依赖-->
    <dependency>
    	<groupId>org.apache.shiro</groupId>
    	<artifactId>shiro-spring</artifactId>
    	<version>1.3.2</version>
    </dependency>
    

1. 案例

1.1 定义ShiroConfig和SwaggerConfig

package com.wpj.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createApi(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.wpj.shiro.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("这里是springboot整合shiro和swagger2前后分离的功能文档")
                .description("这里是nz1904-springboot-08-shiro-swagger测试用的")
                .contact("杰KaMi")
                .version("v1.0")
                .build();
    }

}
package com.wpj.config;

import org.springframework.boot.SpringBootConfiguration;

@SpringBootConfiguration
public class ShiroConfig {

}

1.2 扩展原生Token

package com.wpj.token;

import org.apache.shiro.authc.UsernamePasswordToken;

public class CustomToken extends UsernamePasswordToken {

    private String token;   // 用户身份唯一的标识

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

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

1.3 自定义异常

package com.wpj.exception;

/**
 * 定义业务异常
 */
public class BusinessException extends RuntimeException {
    
    private int messageCode;
    
    private String defaultMessage;
    
    public BusinessException(int messageCode, String defaultMessage){
        super(defaultMessage);
        this.messageCode = messageCode;
        this.defaultMessage = defaultMessage;
    }

    public String getDefaultMessage() {
        return defaultMessage;
    }

    public int getMessageCode() {
        return messageCode;
    }
}

1.4 定义常量类

package com.wpj.constant;

public class Constant {

    public static final String REQ_TOKEN = "token";

}

1.5 自定义过滤器拦截请求

package com.wpj.filter;

import com.alibaba.fastjson.JSONObject;
import com.wpj.constant.Constant;
import com.wpj.exception.BusinessException;
import com.wpj.token.CustomToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;

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

/**
 * 对身份进行二次校验的时候就会进来这里
 */
public class CustomAccessControllerFilter extends AccessControlFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {

        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        try {
            // 校验身份
            // 获取token
            String token = request.getHeader(Constant.REQ_TOKEN);
            // 判断token是否为空
            if (StringUtils.isEmpty(token)) {
                // 身份非法
                throw new BusinessException(400001, "用户的请求token不能为空。");
            } else {
                // 带了token
                // 封装token交给shiro去认证,查看是否合法
                CustomToken customToken = new CustomToken(token);
                // 用户第一次登陆并不会执行,是在认证成功之后访问其他资源的时候才执行
                getSubject(servletRequest, servletResponse).login(customToken);
            }
        } catch (BusinessException e) {
            // 返回json告诉前端出问题了
            resultResponse(e.getMessageCode(), e.getDefaultMessage(), servletResponse);
        } catch (AuthenticationException e) {   // 校验没通过异常
            // e.getCause() 返回当前异常实例
            if(e.getCause() instanceof BusinessException) {
                // 将异常的实例进行转换
                BusinessException err = (BusinessException) e.getCause();
                resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
            } else {
                // 执行这里就说明这个异常是shiro返回的
                resultResponse(400001, "用户认证失败", servletResponse);
            }
        } catch (AuthorizationException e) {
            if(e.getCause() instanceof BusinessException) {
                // 将异常的实例进行转换
                BusinessException err = (BusinessException) e.getCause();
                resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
            } else {
                // 执行这里就说明这个异常是shiro返回的
                resultResponse(403001, "用户没有访问权限", servletResponse);
            }
        } catch (Exception e) { // 一些未考虑的异常
            if(e.getCause() instanceof BusinessException) {
                // 将异常的实例进行转换
                BusinessException err = (BusinessException) e.getCause();
                resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
            } else {
                // 执行这里就说明这个异常是shiro返回的
                resultResponse(500001, "系统出现异常", servletResponse);
            }
        }

        return false;
    }

    /**
     * 告诉前端出错的信息
     * @param messageCode
     * @param defaultMessage
     * @param response
     */
    private void resultResponse(int messageCode, String defaultMessage, ServletResponse response) {
        // 构建返回的数据
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", messageCode);
        jsonObject.put("msg", defaultMessage);

        // 设置下返回类型
        response.setContentType(MediaType.APPLICATION_JSON_UTF8.toString());
        // 获取输出流
        try {
            ServletOutputStream out = response.getOutputStream();
            out.write(jsonObject.toJSONString().getBytes());
            out.flush();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.6 自定义HashedCredentialsMatcher认证

package com.wpj.filter;

import com.wpj.exception.BusinessException;
import com.wpj.token.CustomToken;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;

public class CustomHashedCredentialsMatcher extends HashedCredentialsMatcher {
    /**
     * 认证成功与否
     * @param token
     * @param info
     * @return
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        // 把前面传递的token取出来,再把存储导服务器的token取出来作比较
        // 如果一致就返回true,否则就返回false
        CustomToken customToken = (CustomToken) token;
        // 取出principal的值
        String tokenVal = (String) customToken.getPrincipal();
        // 从Redis或者数据库,session取出信息来
        // 模拟取出来了
        String tokenServlet = "wpj";
        // 进行比较
        if(!(tokenVal.equals(tokenServlet))) {
            // 如果不一致
            throw new BusinessException(401000, "授权信息无效,请重新登录");
        }
        return true;
    }
}
发布了60 篇原创文章 · 获赞 12 · 访问量 6127

猜你喜欢

转载自blog.csdn.net/TheNew_One/article/details/104556208