一个简单的项目Java后端框架(springboot+maven包括shiro,验证码的生成与使用,异常处理,跨域处理)

前言

学习Java后端有一段时间了,最近实验室的官网需要重构,我搭了一个springboot+maven架子给给新入坑的同学使用,包括常见的shiro权限控制,验证码生成与使用,异常处理。跨域处理)。

1创建一个springboot项目

创建一个springboot项目,建立如下结构
在这里插入图片描述

2导入需要的依赖

我们先导入需要的依赖,让maven帮助我们导包,因为项目后期打算引入swagger,所以我把swagger的依赖也引入了。

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.15</version>
    </dependency>
    <dependency>
        <groupId>com.mchange</groupId>
        <artifactId>c3p0</artifactId>
        <version>0.9.5.4</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

    <!-- 日志-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.12</version>
    </dependency>

    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <!-- 测试-->
    <dependency>
        <groupId>org.jmock</groupId>
        <artifactId>jmock-junit4</artifactId>
        <version>2.8.2</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.0</version>
    </dependency>

    <!-- swagger-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.8.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.8.0</version>
    </dependency>
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>swagger-bootstrap-ui</artifactId>
        <version>1.9.5</version>
    </dependency>

    <!-- kaptcha -->
    <dependency>
        <groupId>com.github.penggle</groupId>
        <artifactId>kaptcha</artifactId>
        <version>2.3.2</version>
    </dependency>

    <!--shiro-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.2.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-ehcache</artifactId>
        <version>1.4.0</version>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.0-jre</version>
    </dependency>
</dependencies>  

3配置yml文件

springboot新建的项目里面是application.properties文件但是我个人更喜欢yml文件,把properties的后缀改为yml即可

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/weily?useUnicode=true&charactorEncoding=utf8&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
    username: 
    password: 
    type: com.alibaba.druid.pool.DruidDataSource

#图形验证码
kaptcha:
  border: "yes"
  border.color: 105,179,90
  textproducer:
    font:
      color: blue
      size: 30
      names: 宋体,楷体,微软雅黑
    char:
      length: 4
  image:
    width: 120
    height: 67
  session:
    key: code
    date: 10

server:
  port: 8080
  servlet:
    context-path: /weily

#mybatis
mybatis:
  type-aliases-package: com.website.weily.dao
  mapper-locations: classpath:mapper/*.xml
  config-location: classpath:mybatis-config.xml


#shiro
shiro:
  hashedCredentialsMatcher:
    hashAlgorithmName: md5
    hashIterations: 2
  shiroFilter:
    loginUrl: /user/authc
    unauthorizedUrl: /user/authc
    filterChainDefinitionMap:
      anon:
        image: /img/**
        login: /user/login
        static: /static/
        html: /doc.html
        resources: /swagger-resources
        docs: /v2/api-docs
        ext: /v2/api-docs-ext
        webjars: /webjars/**
      authc:
        other: /**

把mybatis和数据库相关的配置写在配置文件里面,把验证码和shiro需要用到的数据也写在里面,其中mybatis的config文件写在mybatis-config.xml文件中,内容如下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    <!--配置全局属性-->
    <settings>
        <!--使用jdbc的getGeneratedKeys获取数据库自增主键值-->
        <setting name="useGeneratedKeys" value="true"/>

        <!--使用列标签替换列别名 默认true-->
        <setting name="useColumnLabel" value="true"/>

        <!--开启驼峰命名转换-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>

我们现在可以测试一下连接是否正常,在dao层写一个getbyName的方法,测试一下,数据库加上一条数据,这里只是做测试用,随便写几个字段就好
在这里插入图片描述
在这里插入图片描述
测试类写一个测试方法运行
在这里插入图片描述
我们可以看到查出了名为张三的user信息,证明连接数据库成功了,这里需要注意的是要在dao层对应的接口上@Mapper注解扫描这个dao或者在Application中使用@MapperScan(“com.website.weily.dao”)注解扫描整个dao层

4vo类,util类,异常处理类,异常码,常量等的编写

这一步主要是看项目用到什么,不同项目的差异比较大,这里就不做赘述了,只展示下数据返回类的和异常处理,其它部分有兴趣的可以去看源代码。
ap返回数据工具类

public final class ResultUtil {

    public static ResultVo success(Object data) {
        ResultVo result = new ResultVo();
        result.setCode(ErrorCodeEnum.SUCCESS.getCode());
        result.setMsg(ErrorCodeEnum.SUCCESS.getMsg());
        result.setData(data);
        return result;
    }

    public static ResultVo success() {
        return success(null);
    }

    public static ResultVo error(String code, String msg) {
        ResultVo result = new ResultVo();
        result.setCode(code);
        result.setMsg(msg);
        result.setData(null);
        return result;
    }

    public static ResultVo error(ErrorCodeEnum errorCodeEnum) {
        ResultVo result = new ResultVo();
        result.setCode(errorCodeEnum.getCode());
        result.setMsg(errorCodeEnum.getMsg());
        result.setData(null);
        return result;
    }
}

写这个类主要是为了让我们得到的数据都是规范的格式,消息码,信息和数据。
全局异常捕捉

@Slf4j
@ControllerAdvice
public class ExceptionHandle {


    /**
     * 自定义异常
     * @param baseException
     * @return
     */
    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ResultVo handle(BusinessException baseException) {
        log.error("ExceptionHandle#handle(BusinessException) : code={}, msg={}, e={}",
                baseException.getCode(), baseException.getMessage(), baseException);
        return ResultUtil.error(baseException.getCode(),baseException.getMessage());
    }


    /**
     * 未授权异常
     * @param unauthenticatedException
     * @return
     */
    @ExceptionHandler(value = UnauthenticatedException.class)
    @ResponseBody
    public ResultVo handle(UnauthenticatedException unauthenticatedException) {
        log.error("ExceptionHandle#handle(UnauthorizedException) : msg={}, e={}",
                unauthenticatedException.getMessage(),unauthenticatedException);

        return ResultUtil.error(ErrorCodeEnum.NOT_HAVE_PERMISSION);
    }


    /**
     * 其它异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResultVo handle(Exception e) {
        log.error("【系统异常】{}", e);
        return ResultUtil.error(ErrorCodeEnum.UNKNOWN_ERROR);
    }
}

5验证码的生成与使用

我们使用@Value注解读取之前写在yml文件中的验证码配置
验证码生成类

@Component
public class KaptchaConfiguration {

    @Value("${kaptcha.border}")
    private String border;

    @Value("${kaptcha.border.color}")
    private String borderColor;

    @Value("${kaptcha.textproducer.font.color}")
    private String textproducerFontColor;

    @Value("${kaptcha.textproducer.font.size}")
    private String textproducerFontSize;

    @Value("${kaptcha.textproducer.font.names}")
    private String textproducerFontNames;

    @Value("${kaptcha.textproducer.char.length}")
    private String textproducerCharLength;

    @Value("${kaptcha.image.width}")
    private String imageWidth;

    @Value("${kaptcha.image.height}")
    private String imageHeight;

    @Value("${kaptcha.session.key}")
    private String sessionKey;

    @Value("${kaptcha.session.date}")
    private String sessionDate;


    @Bean
    public DefaultKaptcha getDefaultKapcha() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", border);
        properties.setProperty("kaptcha.border.color", borderColor);
        properties.setProperty("kaptcha.textproducer.font.color", textproducerFontColor);
        properties.setProperty("kaptcha.textproducer.font.size", textproducerFontSize);
        properties.setProperty("kaptcha.textproducer.font.names", textproducerFontNames);
        properties.setProperty("kaptcha.textproducer.char.length", textproducerCharLength);
        properties.setProperty("kaptcha.image.width", imageWidth);
        properties.setProperty("kaptcha.image.height", imageHeight);
        properties.setProperty("kaptcha.session.key", sessionKey);
        properties.setProperty("kaptcha.session.date",sessionDate);

        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}

我们再在controller中写一个获取验证码的api接口

@Slf4j
@RestController
@RequestMapping(value = "/img")
public class ImageController {

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @GetMapping(value = "/code",produces = "image/jpg")
    public void setDefaultKaptcha(HttpServletRequest request, HttpServletResponse response){
        // 定义字节数组
        byte[] captchaChallengeAsJpeg;
        // 定义字节输出流
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();

        // 生成四位的验证码
        String createText = defaultKaptcha.createText();
        HttpSession session = request.getSession();
        // 将验证码保存到session中
        session.setAttribute(SessionConstant.IMAGE,createText);
        // 生成图形验证码
        BufferedImage challenge = defaultKaptcha.createImage(createText);
        // 转为byte并放在字节输出数组
        try {
            ImageIO.write(challenge,"jpg",jpegOutputStream);
        } catch (IOException e){
            log.error("ImageController#setDefaultKaptcha: image create error,e={}",e);
        }

        /**
         * 定义response输出类型为image/jpeg类型,使用response输出流输出图片的byte数组
         */
        captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
        response.setHeader("Cache-Control", "no-store");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");
        try {
            ServletOutputStream servletOutputStream = response.getOutputStream();
            servletOutputStream.write(captchaChallengeAsJpeg);
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e) {
            log.error("ImageController#defaultKaptcha: 输出验证码失败, e={}. ", e);
        }
    }


}

启动项目,打开postman,输入url地址
在这里插入图片描述
我们可以看到已经生成验证码了

6shiro的配置

创建一个AuthRealm类用来自定义realm,认证,授权,缓存清理

public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private UserDao userDao;


    /**
     * 为用户授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        //根据前端传入的用户信息封装未user对象
        User user = (User) principals.getPrimaryPrincipal();
        //获取前端传入的用户名
        String username = user.getUsername();
        //根据用户名查询数据库中对应的记录
        User user1 = userDao.findByName(username);
        //如果数据库中有该用户名对应的记录,就进行授权操作
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if (user1 != null) {
            UserRole userRole = user.getUserRole();
            Role role = null;
            if (userRole != null) {
                role = userRole.getRole();
            }
            if (role != null) {
                //为用户授予角色
                info.addRole(role.getName());

                //用户授予权限
                Set<String> permissionCollection = new HashSet<>();
                List<RolePermission> rolePermissionList = role.getRolePermissionList();
                if (rolePermissionList != null) {
                    for (RolePermission rolePermission : rolePermissionList) {
                        if (rolePermission != null) {
                            Permission permission = rolePermission.getPermission();
                            if (permission != null) {
                                permissionCollection.add(permission.getName());
                            }
                        }
                    }
                    info.addStringPermissions(permissionCollection);
                }
            }
            return info;
        } else {
            return info;
        }
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //token携带用户信息
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //获取用户名
        String username = usernamePasswordToken.getUsername();
        //根据用户名查询数据库中对应记录
        User user = userDao.findByName(username);
        if (user == null){
            throw new AuthenticationException();
        }

        //当前realm对象
        String realmName = getName();
        //封装用户信息,构建AuthenticationInfo对象并返回
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),realmName);
        return authenticationInfo;
    }

    /**
     * 重写方法,清除当前用户的的 授权缓存
     *
     * @param principals
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 重写方法,清除当前用户的 认证缓存
     *
     * @param principals
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }
}

shiro的配置

@Configuration
public class ShiroConfiguration {

    /**
     * 加密方式
     */
    @Value("${shiro.hashedCredentialsMatcher.hashAlgorithmName}")
    private String hashAlgorithmName;

    /**
     * 加密迭代次数
     */
    @Value("${shiro.hashedCredentialsMatcher.hashIterations}")
    private int hashIterations;

    /**
     * 登陆url
     */
    @Value("${shiro.shiroFilter.loginUrl}")
    private String loginUrl;

    /**
     * 认证失败的url
     */
    @Value("${shiro.shiroFilter.unauthorizedUrl}")
    private String unauthorizedUrl;

    /**
     * 放行image验证码
     */
    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.image}")
    private String image;


    /**
     * 放行登陆方法
     */
    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.login}")
    private String login;

    /**
     * 放行静态资源
     */
    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.static}")
    private String staticStr;

    /**
     * 需要认证的资源
     */
    @Value("${shiro.shiroFilter.filterChainDefinitionMap.authc.other}")
    private String other;

    /**
     * 放行swagger
     */
    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.html}")
    private String html;

    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.resources}")
    private String resources;

    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.docs}")
    private String docs;

    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.ext}")
    private String ext;

    @Value("${shiro.shiroFilter.filterChainDefinitionMap.anon.webjars}")
    private String webjars;


    /**
     * 密码校验规则HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的 ,
     * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //指定加密方式为MD5
        credentialsMatcher.setHashAlgorithmName(hashAlgorithmName);
        //加密次数
        credentialsMatcher.setHashIterations(hashIterations);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }


    /**
     * @param matcher
     * @return
     */
    @Bean("authRealm")
    @DependsOn("lifecycleBeanPostProcessor")
    public AuthRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        AuthRealm authRealm = new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        authRealm.setCachingEnabled(true);
        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        authRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        authRealm.setAuthorizationCacheName("authorizationCache");
        return authRealm;
    }


    /**
     * 定义安全管理器securityManager,注入自定义的realm
     * @param authRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        //配置 ehcache缓存管理器
        manager.setCacheManager(ehCacheManager());
        manager.setRealm(authRealm);
        return manager;
    }

    /**
     * 定义shiroFilter过滤器并注入securityManager
     * @param manager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置securityManager
        bean.setSecurityManager(manager);
        //设置登录页面
        bean.setLoginUrl(loginUrl);
        //设置未授权跳转的页面
        bean.setUnauthorizedUrl(unauthorizedUrl);


        //定义过滤器
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put(image, "anon");
        filterChainDefinitionMap.put(login, "anon");
        filterChainDefinitionMap.put(staticStr, "anon");
        //放行测试数据
        filterChainDefinitionMap.put("/test/**", "anon");

        // 放行swagger
        filterChainDefinitionMap.put(html, "anon");
        filterChainDefinitionMap.put(resources, "anon");
        filterChainDefinitionMap.put(docs, "anon");
        filterChainDefinitionMap.put(ext, "anon");
        filterChainDefinitionMap.put(webjars, "anon");

        //需要登录访问的资源 , 一般将/**放在最下边
        filterChainDefinitionMap.put(other, "authc");

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

    /**
     * Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理 .
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 配置shiro跟spring的关联
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
     * (可选)
     */
    @Bean("lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * shiro缓存管理器;
     * 需要添加到securityManager中
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return cacheManager;
    }
}

usercontroler

@RestController
@RequestMapping(value = "/user")
@Slf4j
public class UserController {

    @Autowired
    private UserDao userDao;
    /**
     * 用户未登录授权提示
     */
    @GetMapping(value = "/authc")
    public ResultVo authc() {
        throw new BusinessException(ErrorCodeEnum.USER_NOT_LOGIN);
    }

    /**
     * 登录
     */
    @PostMapping(value = "/login")
    public ResultVo login(@RequestParam(value = "username") String username,
                          @RequestParam(value = "password") String password,
                          @RequestParam(value = "imageCode") String imageCode,
                          HttpServletRequest request) {
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password) || StringUtils.isBlank(imageCode)) {
            log.error("UserController#login: param is null. username={},imageCode={}, request={}. ",
                    username, imageCode, request);
            throw new BusinessException(ErrorCodeEnum.DATA_NULL);
        }

        boolean checkCode = SessionUtil.checkImageCode(imageCode,request);
        if (!checkCode) {
            log.error("UserController#login: param is null. username={},imageCode={}, request={}. ",
                    username, imageCode, request);
            throw new BusinessException(ErrorCodeEnum.IMAGE_CODE_ERROR);
        }

        LoginVo loginVo = new LoginVo();

        //用户基本信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
        User user = null;

        try {
            //登录,即身份验证
            subject.login(usernamePasswordToken);
            if (subject.isAuthenticated()) {
                user = (User) subject.getPrincipal();
                request.getSession().setAttribute(SessionConstant.USER,user);
            }
        } catch (UnknownAccountException e) {
            throw new BusinessException(ErrorCodeEnum.NO_USER);
        } catch (IncorrectCredentialsException e) {
            throw new BusinessException(ErrorCodeEnum.PASSWORD_ERROR);
        } catch (AuthenticationException e) {
            log.error("其它错误");
            throw new BusinessException(ErrorCodeEnum.NO_USER);
        }
        loginVo.setUser(user);

        return ResultUtil.success(user);
    }

    /**
     * 此方法用来测试shiro是否配置成功
     * @param id
     * @return
     */
    @PostMapping(value = "/update")
    @RequiresRoles(value = {ShiroConstant.ADMINISTRATOR},logical = Logical.OR)
    public ResultVo update(@RequestParam(value = "id")Long id) {
        boolean flag = userDao.update(id);
        return ResultUtil.success(flag);
    }
}

在usescontroller中写下这几个方法就可以开始测试了,需要注意的是登录是由shiro帮助我们完成的不需要在dao层和service层写对应方法,测试的update方法只是为了从简所以也没写service层,各位自己开发的时候驾驶应该严格按照mvc模式,好了打开postman我们开始测试。
首先不登陆,不登陆shiro会为我们转到use/authc这个方法,我们可以看到未登录提示,
在这里插入图片描述
然后验证码错误,我们可以看到验证码验证是正常的。
在这里插入图片描述
正常登录,我们可以看到张三这条数据的权限是组长,当然用户的密码和salt值因为只是测试的关系我没去管它,实际中应该忽略掉返回的。update这个方法是需要administrator也就是管理员才有权限的,我们用张三的账号访问试一试。
在这里插入图片描述
在这里插入图片描述
我们可以看到权限不足的提示,我们在数据库中将张三改为管理员
在这里插入图片描述
请求成功,证明我们的shiro配置是成功的。
附:数据库权限表(这些表仅做测试用,所以有些不规范。)
user_role
在这里插入图片描述
role
在这里插入图片描述
role_permission
在这里插入图片描述
permission
在这里插入图片描述

7跨域

在每个api前加上

@CrossOrigin(allowCredentials = "true",maxAge = 3600)

即可解决跨域问题,当然也可以自己写跨域处理类。

最后附上代码链接
源码地址:https://github.com/yjzxianyu/weilywebsite

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值