SpringBoot+Shiro+Jwt 前后端分离api脚手架搭建

 

前后端分离后端api环境搭建

需要用到一下技术栈:

  • SpringBoot

  • Shiro

  • Jwt

  • MyBatisPlus

  • Swagger

  • Redis

  • Googlekaptcha (谷歌的验证码插件)

Git:https://gitee.com/jydm520/springbootApi.git

第一步:导入依赖

在初始化好的springboot项目中添加下面的依赖

  
 <!--        mysql驱动程序-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!--        lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
?
    <!--        mybatisplus-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>
    <!--        mybatisplus代码生成器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.4.1</version>
    </dependency>
    <!--生成器模板引擎-->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.28</version>
    </dependency>
?
    <!--        swagger2-->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
    <!-- ui 插件 -->
    <dependency>
        <groupId>com.github.xiaoymin</groupId>
        <artifactId>swagger-bootstrap-ui</artifactId>
        <version>1.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <!--jwt-->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.2</version>
    </dependency>
    <!--        jedis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
        <version>2.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
        <version>2.9.0</version>
    </dependency>
    <!-- springboot shiro -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.1</version>
    </dependency>
    <!-- jwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <!--        验证码图片工具类-->
    <dependency>
        <groupId>com.github.axet</groupId>
        <artifactId>kaptcha</artifactId>
        <version>0.0.9</version>
    </dependency>
  
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
    </dependency>
?
</dependencies>

 

第二步:整合MyBatisPlus

因为上面都已经把依赖导入了下面的所有配置就省略了导入以来的步骤

编写配置文件

@Configuration
@MapperScan("com.jydm.api.Mapper")
public class MybatisPlusConfig {
    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public MybatisPlusInterceptor OptimisticLocker() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
?
}

 

配置据库

Application.yml
spring:
  datasource:
    username: root
    password: niuniu
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver

 

注意以下最新版的mysql驱动需要设置时区不然会时区报错

配置MyBatisPlus日志

Application.yml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

 

这里就可以测试以下MyBatisPlus是不是可以用了

整合代码自动生成工具(可选)

public class AutoCode {
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }
?
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
?
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("jydm");
        gc.setOpen(false);
        gc.setFileOverride(false);//是否覆盖
        gc.setServiceName("%sService");//去除Service I前缀
        gc.setDateType(DateType.ONLY_DATE);//日期类型
        gc.setSwagger2(true); //实体属性 Swagger2 注解
        mpg.setGlobalConfig(gc);
?
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/shop?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("niuniu");
        dsc.setDbType(DbType.MYSQL);//设置数据库类型
        mpg.setDataSource(dsc);
?
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("api");//模块名字
        pc.setParent("com.jydm");//包路径
        pc.setEntity("pojo");//实体类包名
        pc.setMapper("mapper"); //mapper包名
        pc.setService("service"); //service包名
        pc.setController("controller");//controller包名
        mpg.setPackageInfo(pc);
?
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo
                                             tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
?
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
?
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude(scanner("表名"));
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);//自动lombok
        strategy.setRestControllerStyle(true);//controller restful风格
        strategy.setLogicDeleteFieldName("deleted");//逻辑删除
        strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2
        strategy.setVersionFieldName("version");// 乐观锁
        //自动填充配置
        TableFill gmtCreate = new TableFill("create_time", FieldFill.INSERT);//创建时间填充
        TableFill gmtModified = new TableFill("update_time", FieldFill.INSERT_UPDATE);//插入更细时间填充
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(gmtCreate);
        tableFills.add(gmtModified);
        strategy.setTableFillList(tableFills);
        mpg.setStrategy(strategy);
?
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        //启动
        mpg.execute();
    }
}

 

第三步:整合Swagger

编写配置文件

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors
                //controller包路径
                .basePackage("com.jydm.api.controller"))
                //过滤
                //.paths(PathSelectors.ant("/"))
                .build();
        //是否开启
        //.enable(false);
    }
    //界面信息 访问网址/doc.html
    public ApiInfo apiInfo(){
        return  new ApiInfo("简易代码Swagger", "自研自主学习平台", "1.0",
                "urn:tos",
                new Contact("简易代码",
                        "www.baidu.com",
                        "1656641922@qq.com"),
                "Apache 2.0",
                "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
    }

 

swagger还是很好配置的只需要配置这个配置文件

还可以定义识别发布版本关闭swagger这个想弄的网上一查都有这里就不介绍了不是重点

因为我用了ui插件所以访问Swagger的路径为doc.html(个人喜欢比较好看

第四步:Redis

编写配置文件(直接cv

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
//key序列化方式
        template.setKeySerializer(redisSerializer);
//value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
?
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

 

application.yml

 spring:
redis:
#   服务器地址
  host: 192.168.19.100
#   端口号
  port: 6379
#   数据库索引
  database: 0
#   链接超时时间
  timeout: 1800000
  lettuce:
    pool:
#       连接池最大链接数
      max-active: 200
#       最大阻塞等待时间(负数表示没限制)
      max-wait: -1
#       连接池中最大空闲链接
      max-idle: 5
#       连接池中最小空闲连接
      min-idle: 0

这里还可以写一个Redis的工具类 这个太长了直接上文件

第五步:统一结果封装

/**
* 统一结果封装类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;
private String msg;
private Object data=null;
public static Result succ(Object data) {
return succ(200, "操作成功", data);
}
public static Result succ(int code, String msg, Object data)
{
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
public static Result fail(String msg) {
return fail(400, msg, null);
}
public static Result fail(String msg, Object data) {
return fail(400, msg, data);
}
public static Result fail(int code, String msg, Object data) {
Result r = new Result();
r.setCode(code);
r.setMsg(msg);
r.setData(data);
return r;
}
}

第六步:验证码

编写配置文件

@Configuration
public class KaptchaConfig {
    @Bean
    public DefaultKaptcha producer()
    {      Properties properties = new Properties();
    properties.put("kaptcha.border", "no");
    properties.put("kaptcha.textproducer.font.color", "black");
    properties.put("kaptcha.textproducer.char.space", "4");
    properties.put("kaptcha.image.height", "40");
    properties.put("kaptcha.image.width", "120");
    properties.put("kaptcha.textproducer.font.size", "30");
    Config config = new Config(properties);
    DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
    defaultKaptcha.setConfig(config);
    return defaultKaptcha;
    }
}

 

这里就是验证码图片的一些属性

验证码控制器

public class KaptchController {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private Producer producer;
    @GetMapping ("/api/captcha")
    public Result getKaptch(HttpServletRequest request){
    //每次获取前删除原有验证码
          String token = request.getParameter("codeKey");
        if (token!=null){
            redisUtils.hDelete(Const.captcha_KEY,token);
        }
        //生成验证码的key
        String key= UUID.randomUUID().toString();
        //生成验证码
        String code=producer.createText();
       //生成图片
       BufferedImage image=producer.createImage(code);
        ByteArrayOutputStream op=new ByteArrayOutputStream();
        try {
            ImageIO.write(image,"jpg",op);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //将图片转为base64格式
        BASE64Encoder encoder = new BASE64Encoder();
        String str = "data:image/jpeg;base64,";
        String base64Img = str + encoder.encode(op.toByteArray());
        redisTemplate.boundHashOps(Const.captcha_KEY).put(key,code);
        redisTemplate.boundHashOps(Const.captcha_KEY).expire(1, TimeUnit.MINUTES);
        
        log.info("验证码 -- {} - {}", key, code);
        HashMap<Object, Object> map = new HashMap<>();
        map.put("codekey", key);
        map.put("base64Img", base64Img);
        return Result.succ(map);
    }
}

 

验证码hashkey的一个常量

public class Const {
    //redis hash验证码的key
    public final static String captcha_KEY="captcha";
}

 

第七步:Shiro+Jwt

最繁琐的地方,也是前后端分离身份认证最关键的地方

配置Jwt相关:

自定义Token

重写了shiro的HostAuthenticationToken,RememberMeAuthenticationToken类

@Data
public class JwtToken implements HostAuthenticationToken, RememberMeAuthenticationToken {
    @Autowired
    JwtUtils jtUtils;
    private String token;
    private char[] password;
    private boolean rememberMe;
    private String host;
    public JwtToken(String token) {
        this.token = token;
    }
    @Override
    public Object getPrincipal() {
        return token;
    }
    @Override
    public Object getCredentials() {
        return this.password;
    }

    public JwtToken(String token, String password) {
        this(token, (char[])(password != null ? password.toCharArray() : null), false, (String)null);
    }
    public JwtToken(String token, char[] password, boolean rememberMe, String host) {
        this.rememberMe = false;
        this.token = token;
        this.password = password;
        this.rememberMe = rememberMe;
        this.host = host;
    }
}

 

JwtUtils

这里主要是提供一些对jwt的操作

@Data
@Component
@ConfigurationProperties(prefix = "jydm.jwt")//绑定到配置文件 可以在yml给属性初始化值
public class JwtUtils {
    private long expire;
    private String secret;
    private String header;
    // 生成jwt
    public String generateToken(String username) {
        Date nowDate = new Date();
        Date expireDate = new Date(nowDate.getTime() + 1000 * expire);
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setId(username)
                .setSubject(username)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)// 7天過期
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    // 解析jwt
    public Claims getClaimByToken(String jwt) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }
    // jwt是否过期
    public boolean isTokenExpired(Claims claims) {
             return claims.getExpiration().before(new Date());
    }

}

 

JwtFilter

@Slf4j
public class JwtFilter extends AuthenticatingFilter {
     @Resource
     JwtUtils jwtUtils;
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if(StringUtils.isEmpty(jwt)||"null".equals(jwt)) {
            return null;
        }
        return new JwtToken(jwt);
    }
?
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if("null".equals(jwt)||jwt==null||StringUtils.isEmpty(jwt)){
            return true;
        } else {
            // 校验jwt
            Claims claim = jwtUtils.getClaimByToken(jwt);
            if(claim == null || jwtUtils.isTokenExpired(claim)){
               throw new ExpiredCredentialsException("token已失效,请重新登录");//抛出一个异常 进行统一异常处理
            }
            // 执行登录
            return executeLogin(servletRequest, servletResponse);
        }
    }
     //登录失败
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        Throwable throwable = e.getCause() == null ? e : e.getCause();
        String json = e.getMessage();
        try {
            httpServletResponse.getWriter().print(json);
        } catch (IOException ioException) {
        }
        return false;
    }
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }
}

 

 

配置Shiro+Realm

Realm

@Slf4j
@Component
public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;
    @Autowired
    JwtUtils jwtUtils;
    //开启上面自定义的token
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        //获取真证对象
        String principal =(String) subject.getPrincipal();
        User user = userService.getUser(principal);
        log.info("user",user);
        info.addRole(user.getAuthor());
        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        JwtToken usertoken=(JwtToken) token;
        String jwt = usertoken.getToken();
        Claims claim = jwtUtils.getClaimByToken(jwt);
        User login = userService.getUser(claim.getId());
        if (login==null){
            return null; //自动抛出UnknownAccountException异常
        }else {
            return new SimpleAuthenticationInfo(login.getUsername(),login.getPassword(),getName());
        }
?
    }
}

 

ShiroConfig

@Configuration
public class ShiroConfig {
   @Autowired
    private  UserRealm userRealm;
   @Autowired
   private MyCredentialsMatcher myCredentialsMatcher;
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")  DefaultWebSecurityManager manager,JwtFilter jwtFilter){
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(manager);
        //设置jwt过滤器
        Map<String, Filter> map=new LinkedHashMap<>();
        map.put("jwt",jwtFilter);
        filterFactoryBean.setFilters(map);
?
        //权限访问规则
        Map<String, String> filterMap=new LinkedHashMap<>();
        filterMap.put("/api/user/login","anon");//不拦截登录页面
        filterMap.put("/api/user/**","authc");     //访问user下面的页面 需要authc权限
        filterMap.put("/**","jwt");//统一经过jwt过滤器
        filterFactoryBean.setLoginUrl("/error/unLogin");//没有登录跳转到这个地址
        filterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return filterFactoryBean;
    }
    
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        userRealm.setCredentialsMatcher(myCredentialsMatcher);
        //绑定realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }
    //创建jwt过滤器 上面创建的
    @Bean
    public JwtFilter getFilter(){
        return  new JwtFilter();
    }
    // 开启注解代理
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
}

 

自定义密码验证器

因为我们这里用的自定义的token ,原有的那个密码验证器无法解析我们的token中的信息

让shiro走我们自定义的密码验证器(从这里也可以做一些加密的比对)

//自定义密码校验器
@Component
public class MyCredentialsMatcher extends SimpleCredentialsMatcher {
    @Autowired
    private UserService userService;
    //使用自定义token
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        JwtToken jwtToken=(JwtToken) token;
        //这里主要是为了防止密码为空时候报异常,我们这里应该在前端约束密码不能为空
        if (jwtToken.getPassword() == null){
            return true;
        }
        //获得用户输入的密码:
        String inPassword = new String(jwtToken.getPassword());
        //获得用户输入的密码:
        String username = String.valueOf(info.getPrincipals());
        //获得数据库中的密码
        String dbPassword=(String) info.getCredentials();
        //获取盐
        User user = userService.getUser(username);
        //进行密码的比对
        return this.equals(inPassword, dbPassword);
    }
?
}

 

统一异常处理

@RestControllerAdvice
@Slf4j
public class ShiroException {
     @ExceptionHandler(value = UnauthorizedException.class)
    public Result handler(){
        return Result.fail("权限不足");
    }
    @ExceptionHandler(value = ExpiredCredentialsException.class)
    public Result handler(ExpiredCredentialsException e) {
        log.error("运行时异常:----------------{}", e.getMessage());
        return Result.fail("登录已过期,请重新登录");
    }
    @ExceptionHandler(value = UnauthenticatedException.class)
    public Result handler(UnauthenticatedException e) {
        log.error("运行时异常:----------------{}", e);
        return Result.fail("未登录");
    }
    @ExceptionHandler(value = UnknownAccountException.class)
    public Result handler(UnknownAccountException e) {
        log.error("运行时异常:----------------{}", e);
        return Result.fail("未登录");
    }
}

 

SpringBoot+Shiro+Jwt 前后端分离api脚手架搭建

上一篇:低代码行业风涌云起,他们靠什么跻身全球第一阵营?


下一篇:Markdown的使用