Shiro的使用(六)—SpringBoot整合shiro和Swagger2實現前後端分離

通過擴展AccessControlFilter,HashedCredentialsMatcher完成了自定義身份校驗器,訪問控制過濾器等核心技術。
使用了全局業務異常,處理項目中可能出現的異常信息,並使用了枚舉定義輸出信息。
封裝了通用的返回結果集,可在實際開發項目中,直接進行使用。

0、流程圖、思路分析

在這裏插入圖片描述

思路

首先看圖,先理解整合shiro之後如何實現前後端分離的項目

看完圖我們會發現幾個問題

  1. 爲什麼不使用shiro進行身份認證?

    1. 因爲這是前後端分離項目,原本的html頁面可能和後臺並不在同一個項目中,使用shiro是訪問不到html頁面的
  2. 如何儲存用戶認證成功生成的 token ?

    1. token 可以儲存在session、redis、數據庫中
    2. 這裏採用 儲存在數據庫中的方式,給數據中添加一個字段 token
    3. 再設置上 token 的過期時間,添加一個字段 expireDate
  3. 登錄成功後,再次訪問資源的時候,如何校驗身份?

    1. 不能使用shiro原本的校驗方式,原本校驗是通過校驗密碼實現的,現在傳遞的驗證方式只有token
    2. 所以我們需要重寫 密碼校驗器的方法,實現我們自己的驗證方式。

1、代碼實現

1.1、導入依賴

`<dependencies>
        <!-- springboot對緩存的支持-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- ehcache緩存核心 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.10.4</version>
        </dependency>
        <!-- shiro對ehcache緩存的支持 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</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>

        <!-- druid連接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>

        <!-- spring對shiro的支持 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

1.2、配置緩存文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="D:\mytemp"/>
    <cache name="users"
           timeToLiveSeconds="300"
           maxEntriesLocalHeap="1000"/>
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="120"
                  timeToLiveSeconds="120"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>
</ehcache>

1.3、配置application.properties

server.port=8080
spring.application.name=springboot
# mybatis配置
mybatis.type-aliases-package=com.fu.springboot.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
# druid連接池配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///demo
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

1.4、編寫swaggerConfig

@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {
    private Logger logger = LoggerFactory.getLogger(SwaggerConfig.class);

    @Bean
    public Docket docket() {
        logger.info("Swagger2 ---> docket 執行了...");
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo()) // 生成接口文檔的頭部信息
                .select()           // 表示選擇哪些路徑和API生成文檔,這裏是所有
                .apis(RequestHandlerSelectors.basePackage("com.fu.springboot.controller"))  // 指定接口所在的包
                .paths(PathSelectors.any())     // 表示對所有的API進行監控
                .build();
    }
    /**
     * 接口文檔的頭部信息
     */
    private ApiInfo apiInfo() {
        logger.info("Swagger2 ---> apiInfo 執行了...");
        Contact contact = new Contact("孔明", "暫無url", "[email protected]");
        return new ApiInfoBuilder()
                .title("SpringBoot整合shiro+Swagger2實現前後端分離")
                .description("文檔描述")
                .contact(contact)
                .version("v1.0")
                .build();
    }
}

1.5、編寫實體類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private static final long serialVersionUID = 8142836626401616290L;

    private Integer id;
    private String name;
    private String password;
    private String token;
    private Date expireDate;
}

1.6、編寫返回結果集

public interface ResponseCodeInterface {
    /**
     *  獲取返回碼
     */
    int getCode();

    /**
     *  獲取返回的消息
     */
    String getMsg();
}
public enum  BaseResponseCode implements ResponseCodeInterface {
    SUCCESS(0, "操作成功"),
    SYSTEM_ERROR(500001, "系統錯誤"),
    METHOD_INVALIDATE(400001, "數據校驗出錯"),
    DATA_ERROR(400002, "傳入數據異常"),
    TOKEN_NOT_NULL(401001, "用戶token不存在,請重新登錄"),
    TOKEN_ERROR(500002, "用戶身份校驗失敗,請重新登錄");

    private int code;
    private String msg;

    BaseResponseCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    @Override
    public int getCode() {
        return this.code;
    }

    @Override
    public String getMsg() {
        return this.msg;
    }
}
@Data
public class DataResult<T> {
    /**
     * 碼值
     */
    private int code = 0;
    /**
     * 返回的錯誤信息
     */
    private String msg = "";
    /**
     *  返回的數據
     */
    private T data;

    // 封裝構造器
    public DataResult(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public DataResult(int code, T data) {
        this.code = code;
        this.data = data;
    }

    public DataResult(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public DataResult(){
        this.code = BaseResponseCode.SUCCESS.getCode();
        this.msg = BaseResponseCode.SUCCESS.getMsg();
        this.data = null;
    }

    public DataResult(T data){
        this.code=BaseResponseCode.SUCCESS.getCode();
        this.msg=BaseResponseCode.SUCCESS.getMsg();
        this.data=data;
    }

    public DataResult(ResponseCodeInterface responseCodeInterface) {
        this.code = responseCodeInterface.getCode();
        this.msg = responseCodeInterface.getMsg();
    }

    public DataResult(ResponseCodeInterface responseCodeInterface, T data) {
        this.code = responseCodeInterface.getCode();
        this.msg = responseCodeInterface.getMsg();
        this.data = data;
    }

    // 不帶數據的返回值信息
    public static <T>DataResult success() {
        return new DataResult();
    }
    // 帶數據的返回值
    public static <T>DataResult success(T data) {
        return new <T>DataResult(data);
    }
    // 3個參數的返回值
    public static <T>DataResult getResult(int code, String msg, T data) {
        return new <T>DataResult(code, msg, data);
    }
    // 2個參數的返回值(碼值,提示信息)
    public static <T>DataResult getResult(int code, String msg) {
        return new <T>DataResult(code, msg);
    }
    // 2個參數的返回值(碼值,用戶信息)
    public static <T>DataResult getResult(int code, T data) {
        return new <T>DataResult(code, data);
    }
    /**
     *  直接傳遞一個枚舉類型
     */
    public static <T>DataResult getResult(BaseResponseCode baseResponseCode) {
        return new <T>DataResult(baseResponseCode);
    }
    public static <T>DataResult getResult(BaseResponseCode baseResponseCode, T data) {
        return new <T>DataResult(baseResponseCode, data);
    }
}

1.7、編寫全局業務異常

public class BusinessException extends RuntimeException {
    private static final long serialVersionUID = 3618625760384608631L;

    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.8、編寫mapper

public interface UserMapper {
    /**
     *  通過用戶名查詢用戶
     */
    User getUserByName(String name);
    /**
     * 查詢所有的用戶
     */
    List<User> getUserAll();
    /**
     *  更新數據庫用戶的token
     */
    void updateUser(User user);
    /**
     *  查看token是否存在
     */
    User getUserByToken(String token);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fu.springboot.mapper.UserMapper">

    <!-- 通過用戶名查詢用戶 -->
    <select id="getUserByName" resultType="user">
        select * from user where name = #{name}
    </select>

    <!-- 查詢所有的用戶 List<User> getUserAll(); -->
    <select id="getUserAll" resultType="user">
        select * from user
    </select>

    <!-- 更新數據庫用戶的token void updateToken(User user); -->
    <update id="updateUser" parameterType="user">
        update user set token = #{token}, expireDate = #{expireDate} where id = #{id}
    </update>

    <!-- 查看token是否存在  User getUserByToken(String token); -->
    <select id="getUserByToken" resultType="user">
        select * from user where token = #{token}
    </select>
</mapper>

1.9、編寫service

public interface UserService {
    /**
     * 根據用戶名獲取用戶信息
     */
    User getUserByName(String name);
    /**
     *  登錄
     */
    User login(User user);
    /**
     * 更新用戶信息
     */
    void updateUser(User user);
    /**
     * 查詢所有的用戶
     */
    List<User> getUserList()throws Exception;
    /**
     * 判定這個token是否存在
     */
    boolean tokenExistsOrNot(String token);
}
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getUserByName(String name) {
        return userMapper.getUserByName(name);
    }

    /**
     *  登錄
     * 第一步:獲取到前端傳遞過來的用戶名
     * 第二步:通過用戶名 獲取用戶對象
     * 第三步:校驗
     * 第四步:生成token保存到數據庫
     * 第五步:將token封裝到返回數據裏面給前端
     */
    @Override
    public User login(User user) {
        String name = user.getName();
        User userResult = this.getUserByName(name);
        if (null == userResult) {   // 說明用戶名不對
            throw new BusinessException(400001, "用戶名不正確");
        }
        if (!userResult.getPassword().equals(user.getPassword())) {
            throw new BusinessException(400002, "密碼錯誤");
        }
        // 生成token,這裏用UUID表示
        String token = UUID.randomUUID().toString().substring(0, 30);
        Date date = new Date();
        userResult.setToken(token);
        userResult.setExpireDate(date);
        // 更新數據庫的數據
        this.updateUser(userResult);
        userResult.setPassword("");
        return userResult;
    }

    @Override
    public void updateUser(User user) {
        userMapper.updateUser(user);
    }
    @Override
    public List<User> getUserList() throws Exception {
        return userMapper.getUserAll();
    }
    @Override
    public boolean tokenExistsOrNot(String token) {
        try {
            User user = userMapper.getUserByToken(token);
            if (null != user) {
                return true;
            }
        } catch (Exception e) {
            return false;
        }
        return false;
    }
}

1.10、編寫controller

@RestController
@Api(tags = {"用戶接口"})
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("login")
    @ApiOperation(value = "用戶登錄的接口")
    public DataResult<User> login(@RequestBody User user) {
        User user1 = userService.login(user);
        DataResult<User> result = null;
        try {
            result = DataResult.success(user1);
        } catch (Exception e) {
            if (e instanceof BusinessException) {
                BusinessException err = (BusinessException) e;
                result = DataResult.getResult(err.getMessageCode(), err.getMessage());
            } else {
                result = DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(), BaseResponseCode.SYSTEM_ERROR.getMsg());
            }
            return result;
        }
        return result;
    }

    @GetMapping("list")
    @ApiOperation("獲取所有用戶的信息")
    @ApiImplicitParam(paramType = "header", name = "token", value = "驗證身份的token", required = true, dataType = "string")
    public Object getUserList() {
        DataResult<List<User>> result = null;
        try {
            List<User> list = userService.getUserList();
            result = DataResult.success(list);
        } catch (Exception e) {
            e.printStackTrace();
            result = DataResult.getResult(BaseResponseCode.SYSTEM_ERROR.getCode(), BaseResponseCode.SYSTEM_ERROR.getMsg());
        }
        return result;
    }
}

1.11、自定義token

public class CustomToken extends UsernamePasswordToken {
    private static final long serialVersionUID = 561721881796304836L;

    /**
     * 用戶身份唯一的標識
     *     這個token是在認證通過之後,用戶訪問其他資源的時候,來進行身份識別的
     */
    private String token;

    /**
     *  只允許在 CustomToken構造的時候給token賦值
     */
    public CustomToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        // 在用戶認證通過之後 再訪問這個方法 默認返回的是 Realm校驗的第一個參數
        // Realm校驗我們是自己定義的,我們可以自己設置這個方法的返回值
        return this.token;
    }
}

1.12、編寫UserRealm

public class UserRealm extends AuthorizingRealm {
    private Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Override
    public String getName() {
        return "UserRealm";
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 取出前端傳遞過來的token
        CustomToken customToken = (CustomToken) authenticationToken;
        String token = (String) customToken.getPrincipal();
        // 將前端傳遞過來的token封裝到 SimpleAuthenticationInfo 對象中
        SimpleAuthenticationInfo simpleAuthorizationInfo = new SimpleAuthenticationInfo(token, token, getName());
        logger.info("UserRealm ---> 認證身份信息執行...");
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        logger.info("UserRealm ---> 授權方法執行...");
        return simpleAuthorizationInfo;
    }
}

1.13、自定義身份校驗器

public class CustomHashedCredentialsMatcher extends HashedCredentialsMatcher {

    @Autowired
    private UserService userService;

    /**
     * @return 返回true代表校驗成功,false代表失敗
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        // 獲取客戶端傳過來的token
        CustomToken customToken = (CustomToken) token;
        String tokenClient = (String) customToken.getPrincipal();
        // 獲取從服務器獲取的token(redis,數據庫 或者 session)
        boolean b = false;
        try {
            b = userService.tokenExistsOrNot(tokenClient);
        } catch (Exception e) {
            throw new BusinessException(BaseResponseCode.TOKEN_NOT_NULL.getCode(), BaseResponseCode.TOKEN_NOT_NULL.getMsg());
        }
        // 判斷token是否一致
        if (!b) {
            throw new BusinessException(BaseResponseCode.TOKEN_ERROR.getCode(), BaseResponseCode.TOKEN_ERROR.getMsg());
        }
        return true;
    }
}

1.14、自定義訪問控制過濾器

public class CustomAccessControlFilter 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 {
            // 1. 獲取token
            String token = request.getHeader(Constant.REQ_TOKEN);
            // 2. 判斷 token 是否爲空
            if (StringUtils.isEmpty(token)) { // 用戶的身份是非法的
                throw new BusinessException(400004, "用戶請求的token不能爲空");
            } else {    // 用戶已經登錄,並獲取到了token
                // 3. 封裝token
                CustomToken customToken = new CustomToken(token);
                // 4. 把token交給shiro做認證,判斷身份是否合法
                /*  這個方法,用戶第一次訪問請求(即 登錄)的時候,並不會執行
                    只有在認證成功之後訪問其他資源的時候,纔會執行
                    作用是:校驗用戶身份,而不是登錄
                 */
                getSubject(servletRequest, servletResponse).login(customToken);
                return true;
            }
        } 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 {
                resultResponse(403001, "用戶沒有訪問權限", servletResponse);
            }
        } catch (Exception e) { // 捕獲未考慮到的異常,比如系統異常
            if (e.getCause() instanceof BusinessException) {
                BusinessException err = (BusinessException) e.getCause();
                resultResponse(err.getMessageCode(), err.getDefaultMessage(), servletResponse);
            } else {
                resultResponse(500001, "服務器開小差了,系統出錯", servletResponse);
            }
        }
        return false;
    }

    /**
     * 這個方法的主要功能就是告訴客戶端 一些出錯的信息
     */
    private void resultResponse(int messageCode, String defaultMessage, ServletResponse servletResponse) {
        // 構建返回的數據
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", messageCode);
        jsonObject.put("msg", defaultMessage);
        // 設置返回的數據類型
        /*  MediaType.APPLICATION_JSON_UTF8_VALUE ===>>> MediaType.APPLICATION_JSON_VALUE
            MediaType.APPLICATION_JSON_UTF8_VALUE 已被標記@Deprecated
            自Spring Framework 5.2起不推薦使用,而推薦使用{@link #APPLICATION_JSON_VALUE}
            由於主要的瀏覽器(例如Chrome)現在已符合規範並正確解釋了UTF-8特殊字符 不需要{@code charset = UTF-8}參數。
         */
        servletResponse.setContentType(MediaType.APPLICATION_JSON.toString());
        // 獲取輸出流
        try {
            ServletOutputStream out = servletResponse.getOutputStream();
            // 將數據寫出去
            out.write(jsonObject.toJSONString().getBytes());
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.15、編寫shiroConfig

@SpringBootConfiguration
public class ShiroConfig {
    private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);

    // 自定義密碼認證器
    @Bean
    public CustomHashedCredentialsMatcher customHashedCredentialsMatcher() {
        CustomHashedCredentialsMatcher hashedCredentialsMatcher = new CustomHashedCredentialsMatcher();
        logger.info("shiro ---> HashedCredentialsMatcher 執行了...");
        return hashedCredentialsMatcher;
    }
    // 用戶Realm
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(customHashedCredentialsMatcher());
        logger.info("shiro ---> UserRealm 執行了...");
        return userRealm;
    }
    // 安全管理器
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm());
        defaultWebSecurityManager.setCacheManager(ehCacheManager());
        logger.info("shiro ---> SecurityManager 執行了...");
        return defaultWebSecurityManager;
    }
    // shiro的過濾器
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 配置自定義的校驗身份的過濾器
        LinkedHashMap<String, Filter> customAccessControlFilter = new LinkedHashMap<>();
        customAccessControlFilter.put("token", new CustomAccessControlFilter());
        shiroFilterFactoryBean.setFilters(customAccessControlFilter);
        // 配置攔截訪問路徑的過濾器
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put("/user/login", "anon");
        map.put("/swagger/**","anon");
        map.put("/v2/api-docs","anon");
        map.put("/swagger-ui.html","anon");
        map.put("/swagger-resources/**","anon");
        map.put("/webjars/**","anon");
        map.put("/favicon.ico","anon");
        map.put("/captcha.jpg","anon");
        map.put("/csrf","anon");
        map.put("/**", "token,authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        logger.info("shiro ---> ShiroFilterFactoryBean 執行了...");
        return shiroFilterFactoryBean;
    }
    // 緩存管理器
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        logger.info("shiro ---> EhCacheManager 執行了...");
        return ehCacheManager;
    }
    /**
     * 開啓aop的註解的支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        attributeSourceAdvisor.setSecurityManager(securityManager);
        return attributeSourceAdvisor;
    }
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
}

1.16、編寫AppConfig

@SpringBootApplication
@ComponentScan("com.fu.springboot")
@MapperScan("com.fu.springboot.mapper")
public class AppConfig {
}

1.17、測試

訪問 http://127.0.0.1:8080/swagger-ui.html ,在頁面進行接口測試即可

3、總結

  1. 首先理解文章開頭的流程圖,理解那幾個問題,然後才能對項目有清晰的思路
  2. 重點是那兩個過濾器(身份校驗器,訪問流程過濾器),還有返回結果集的封裝,一定要理解清除,那是本項目的核心所在
  3. 本項目還有很多不足之處,可自行拓展
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章