Shiro的使用(六)—SpringBoot整合shiro和Swagger2实现前后端分离

发布时间:2024-12-19 09:01

通过扩展AccessControlFilter,HashedCredentialsMatcher完成了自定义身份校验器,访问控制过滤器等核心技术。
使用了全局业务异常,处理项目中可能出现的异常信息,并使用了枚举定义输出信息。
封装了通用的返回结果集,可在实际开发项目中,直接进行使用。

文章目录

      • 0、流程图、思路分析
        • 思路
      • 1、代码实现
        • 1.1、导入依赖
        • 1.2、配置缓存文件
        • 1.3、配置application.properties
        • 1.4、编写swaggerConfig
        • 1.5、编写实体类
        • 1.6、编写返回结果集
        • 1.7、编写全局业务异常
        • 1.8、编写mapper
        • 1.9、编写service
        • 1.10、编写controller
        • 1.11、自定义token
        • 1.12、编写UserRealm
        • 1.13、自定义身份校验器
        • 1.14、自定义访问控制过滤器
        • 1.15、编写shiroConfig
        • 1.16、编写AppConfig
        • 1.17、测试
      • 3、总结

0、流程图、思路分析

\"Shiro的使用(六)—SpringBoot整合shiro和Swagger2实现前后端分离_第1张图片\"

思路

首先看图,先理解整合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>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-cacheartifactId>
        dependency>
        
        <dependency>
            <groupId>net.sf.ehcachegroupId>
            <artifactId>ehcacheartifactId>
            <version>2.10.4version>
        dependency>
        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-ehcacheartifactId>
            <version>1.4.0version>
        dependency>

        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>2.7.0version>
        dependency>
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>2.7.0version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druidartifactId>
            <version>1.1.16version>
        dependency>

        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.62version>
        dependency>

        
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-springartifactId>
            <version>1.4.0version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.mybatis.spring.bootgroupId>
            <artifactId>mybatis-spring-boot-starterartifactId>
            <version>2.1.1version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-devtoolsartifactId>
            <scope>runtimescope>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
            <version>5.1.47version>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintagegroupId>
                    <artifactId>junit-vintage-engineartifactId>
                exclusion>
            exclusions>
        dependency>
    dependencies>

1.2、配置缓存文件


<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\", \"fyf980921@163.com\");
        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);
}


<mapper namespace=\"com.fu.springboot.mapper.UserMapper\">

    
    <select id=\"getUserByName\" resultType=\"user\">
        select * from user where name = #{name}
    select>

    
    <select id=\"getUserAll\" resultType=\"user\">
        select * from user
    select>

    
    <update id=\"updateUser\" parameterType=\"user\">
        update user set token = #{token}, expireDate = #{expireDate} where id = #{id}
    update>

    
    <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. 本项目还有很多不足之处,可自行拓展

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号