首页技术栈归档照片墙音乐日记随想收藏夹友链留言关于

Vue+SpringCloudAlibaba+JWT+Spring-security实现简单登录和CURD

写作时间:2026-07-08

Vue+SpringCloudAlibaba+JWT+Spring-security实现简单登录和CURD

父模块的依赖

    <properties>
        <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.7.RELEASE</spring-cloud-alibaba.version>
        <java.version>1.8</java.version>
        <mybatis-version>3.4.0</mybatis-version>
        <druid-version>1.1.10</druid-version>
        <mysql-version>8.0.28</mysql-version>
        <jwt-version>3.8.0</jwt-version>
        <swagger-version>2.9.2</swagger-version>
        <test-version>2.6.7</test-version>
        <web-version>2.6.7</web-version>
        <lobomk-version>1.18.24</lobomk-version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--mybatis-plus-->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>${mybatis-version}</version>
            </dependency>
            <!--druid 数据源 阿里巴巴提供的一款性能非常高的数据源-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>${druid-version}</version>
            </dependency>

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>${web-version}</version>
            </dependency>

            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql-version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lobomk-version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <version>${test-version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>${swagger-version}</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>${swagger-version}</version>
            </dependency>
            <!--jwt-->
            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>${jwt-version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

yuan-utils工具模块

JWTUtil

/**
 * @author mo
 * @Description: Jwt工具类,用于生成和校验jwt
 */
public class JWTUtil {

    /**过期时间为半小时*/
    private static final long EXPRIE_TIME = 30*60*1000;//30分钟
    /**加密时的密钥,不能泄露*/
    private static final String secret = "B8B7EDFB-53AC-2BD2-E804-461A0C961F29";

    /**
     * 校验用户名和密码
     * @param token
     * @param username
     * @return
     */
    public static boolean verify(String token) {
        try {
            // 加密方式必须与生成token时的加密方式一致
            Algorithm algorithm = Algorithm.HMAC512(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 获取token中的信息无需secret解密也能获得
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 获取token中的信息无需secret解密也能获得
     * @param token
     * @return
     */
    public static String getUserId(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("userId").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成JWTToken自定义 ItripUserDetails 用于封装用户信息
     * @param username
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String sign(String username,String userId) throws
            UnsupportedEncodingException {
        Date date = new Date(System.currentTimeMillis()+EXPRIE_TIME);
        Algorithm algorithm = Algorithm.HMAC512(secret);
        // 附带username信息
        return JWT.create()
                .withClaim("username", username)
                .withClaim("userId", userId)
                .withExpiresAt(date)
                .sign(algorithm);
    }

}

yuan-beans公共模块

依赖

<dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
        </dependency>
    </dependencies>

dto包

package com.yuan.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
public class R {
    @ApiModelProperty(value = "是否成功标示,true:成功;false:失败")
    private String success;
    @ApiModelProperty(value = "异常码")
    private String errorCode;
    @ApiModelProperty(value = "响应提示信息")
    private String msg;
    @ApiModelProperty(value = "返回数据")
    private Object data;

    //把构造方法私有
    private R(){}


    //成功静态方法
    public static R ok(){
        R r = new R();
        r.setSuccess("true");
        r.setErrorCode(ResultCode.SUCCESS);
        r.setMsg("成功");
        return r;
    }

    //失败静态方法
    public static R error(){
        R r = new R();
        r.setSuccess("false");
        r.setErrorCode(ResultCode.ERROR);
        r.setMsg("失败");
        return r;
    }

    public R success(String success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMsg(message);
        return this;
    }

    public R code(String code){
        this.setErrorCode(code);
        return this;
    }

    public  R data(Object data){
        this.setData(data);
        return this;
    }

}
package com.yuan.dto;

public interface ResultCode {

    public static String SUCCESS = "00000";  //成功
    public static String ERROR = "20001";  //失败
}

handler包

/**
 * 自动填充
 * @author 111
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("creationDate",new Date(),metaObject);
        this.setFieldValByName("modifyDate",new Date(),metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("modifyDate",new Date(),metaObject);
    }
}
@Configuration
public class PageConfig {
    // 配置分页插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptorPage() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

pojo包


/**
    * 新闻表
    */
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "news")
public class News {
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    /**
     * 新闻类型
     */
    @TableField(value = "type_id")
    private String typeId;
    /**
     * 新闻标题
     */
    @TableField(value = "title")
    private String title;

    /**
     * 发布者
     */
    @TableField(value = "publisher")
    private String publisher;
    /**
     * 发布者头像
     */
    @TableField(value = "publisher_face")
    private String publisherFace;

    /**
     * 新闻内容
     */
    @TableField(value = "content")
    private String content;
    /**
     * 发布时间
     */
    @TableField(value = "create_time")
    private Date createTime;
    /**
     * 修改时间
     */
    @TableField(value = "update_time")
    private Date updateTime;
    /**
     * 删除状态
     */
    @TableField(value = "deleted")
    private Integer deleted;

    @TableField(value = "typeId")
    private String typeid;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "`type`")
public class Type {
    @TableId(value = "id", type = IdType.INPUT)
    private String id;

    @TableField(value = "name")
    private String name;

}
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "users")
public class Users {
    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    /**
     * 姓名
     */
    @TableField(value = "u_user_name")
    private String uUserName;

    /**
     * 年龄
     */
    @TableField(value = "age")
    private Integer age;

    /**
     * 优先
     */
    @TableField(value = "u_email")
    private String uEmail;

    @TableField(value = "password")
    private String password;

}

Vo包

/**
 * @author mo
 * @Description: token响应对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "Token响应对象")
@Builder
public class TokenVo implements Serializable {

    private static final long serialVersionUID = 1959653550240034417L;

    @ApiModelProperty(value = "用户认证凭证")
    private String token;
    @ApiModelProperty(value = "过期时间,单位毫秒")
    private Long expTime;
    @ApiModelProperty(value = "生成时间,单位毫秒")
    private Long genTime;



}

yuan-dao数据访问层模块

依赖

    </properties>
    <dependencies>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.yuan-utils</groupId>
            <artifactId>yuan-utils</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.yuan-bean</groupId>
            <artifactId>yuan-beans</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

mapper

package com.yuan.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yuan.pojo.News;

public interface NewsMapper extends BaseMapper<News> {
}
package com.yuan.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yuan.pojo.Type;
public interface TypeMapper extends BaseMapper<Type> {
}
package com.yuan.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yuan.pojo.Users;
public interface UsersMapper extends BaseMapper<Users> {
}

由于是单表查询XML文件没有写SQL的配置使用Mybatis-plus

service

package com.yuan.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yuan.pojo.News;
public interface NewsService extends IService<News> {

}
package com.yuan.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuan.mapper.NewsMapper;
import com.yuan.pojo.News;
import com.yuan.service.NewsService;
import org.springframework.stereotype.Service;
@Service
public class NewsServiceImpl extends ServiceImpl<NewsMapper, News> implements NewsService {

}
package com.yuan.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yuan.pojo.Type;
public interface TypeService extends IService<Type> {

}
package com.yuan.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuan.mapper.TypeMapper;
import com.yuan.pojo.Type;
import com.yuan.service.TypeService;
import org.springframework.stereotype.Service;
@Service
public class TypeServiceImpl extends ServiceImpl<TypeMapper, Type> implements TypeService {

}
package com.yuan.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yuan.pojo.Users;
public interface UsersService extends IService<Users> {


}
package com.yuan.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yuan.mapper.UsersMapper;
import com.yuan.pojo.Users;
import com.yuan.service.UsersService;
import org.springframework.stereotype.Service;
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements UsersService {

}

yuan-login登录模块

依赖

    <dependencies>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
        <!--druid 数据源 阿里巴巴提供的一款性能非常高的数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.yuan-dao</groupId>
            <artifactId>yuan-dao</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.yuan-utils</groupId>
            <artifactId>yuan-utils</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.yuan-bean</groupId>
            <artifactId>yuan-beans</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <!--jwt-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>${jwt-version}</version>
        </dependency>
    </dependencies>

application.properties

#配置数据源 druid  springboot默认的数据源 HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/newsdb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=root
#指定数据源的类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#日志的实现类
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#已删除的值
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除的值
mybatis-plus.global-config.db-config.logic-not-delete-value=0

mybatis-plus.mapper-locations=classpath:com/yuan/mapper/*.xml

server.port=8081

spring.application.name=yuan-login

SecurityConfig

/**
 * Spring Security
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/user/**").permitAll();
//                .antMatchers("/news/**").hasRole("vip");
        http.formLogin().loginPage("/user/login");
        http.csrf().disable();//关闭//csrf功能  登出失败的可能的原因
        http.rememberMe();
    }

//    //认证
//    //BCryptPasswordEncoder密码加密算法
//    //  .withUser("用户名").password(new BCryptPasswordEncoder().encode("密码")).roles("权限访问的页面")
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//                .withUser("李四").password("123456").roles("vip");
//    }
}

UserController

package com.yuan.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yuan.dto.R;
import com.yuan.dto.ResultCode;
import com.yuan.pojo.Users;
import com.yuan.service.UsersService;
import com.yuan.utils.JWTUtil;
import com.yuan.vo.TokenVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

@RestController
public class UserController {

    @Autowired
    private UsersService usersService;

    @RequestMapping("/user/login")
    public String login(){
        return "login";
    }
    private static final long EXPRIVE_TIME=30*60*1000;

    @RequestMapping("/user/doLogin")
    @ResponseBody
    public Object doLogin(@RequestParam("username") String username, @RequestParam("password") String password,
                          HttpServletResponse response
    ) throws IOException {
        QueryWrapper<Users> queryWrapper = new QueryWrapper<Users>();
        queryWrapper.eq("u_user_name",username);
        queryWrapper.eq("password",password);


        Users users = usersService.getOne(queryWrapper);
//       if (users != null) {
//           String token = JWTUtil.sign(users.getUUserName(), users.getPassword());
//           response.setHeader("token",token);
//           return token;
//       }
        if (users != null) {
            String token = JWTUtil.sign(users.getUUserName(), String.valueOf(users.getId()));
            TokenVo tokenVo=TokenVo.builder()
                    .token(token)
                    .genTime(System.currentTimeMillis())
                    .expTime(System.currentTimeMillis()+EXPRIVE_TIME).build();

            R data = R.ok().data(tokenVo);
            //转json
            String tokenVoStr = new ObjectMapper().writeValueAsString(data);
            //设置编码格式
            response.setContentType("application/json;charset=utf-8");
            //通过response
            response.setHeader("token", token);
            response.setHeader("Access-Control-Expose-Headers","token");
            PrintWriter out= new PrintWriter(new OutputStreamWriter(response.getOutputStream()));
            out.write(tokenVoStr);
            out.flush();
            out.close();
        }

        return ResultCode.ERROR;
    }
}

启动类

@SpringBootApplication
@MapperScan("com.yuan.mapper")
public class YuanLoginApplication {

    public static void main(String[] args) {
        SpringApplication.run(YuanLoginApplication.class, args);
    }

}

yuan-biz页面模块

依赖

    <dependencies>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!--druid 数据源 阿里巴巴提供的一款性能非常高的数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>com.yuan-bean</groupId>
            <artifactId>yuan-beans</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.yuan-dao</groupId>
            <artifactId>yuan-dao</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.yuan-utils</groupId>
            <artifactId>yuan-utils</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

NewsController

package com.yuan.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yuan.pojo.News;
import com.yuan.service.NewsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
public class NewsController {

    @Autowired
    private NewsService newsService;

    /**
     * 查询接口
     * @param pageNum
     * @param pageSize
     * @return
     */
    @RequestMapping("/news/list")
    @ResponseBody
    public Object list(@RequestParam(value = "pageNum",defaultValue = "1",required = false) Integer pageNum,
                           @RequestParam(value = "pageSize", defaultValue = "3", required = false) Integer pageSize) {
        Page<News> page = new Page<>(pageNum, pageSize);
        Page<News> userPage = newsService.page(page);
        return userPage;
    }

    /**
     * 新增
     * @param news
     * @return
     */
    @RequestMapping("/news/add")
    public Object NewsAdd(@RequestBody News news){
      return   newsService.save(news);
    }

    /**
     * 删除
     * @param id
     * @return
     */
    @RequestMapping("/news/delete")
    public Object delete(@RequestParam("id") Integer id){
        return newsService.removeById(id);
    }

    /**
     * 修改
     * @param news
     * @return
     */
    @RequestMapping("/news/update")
   public boolean update(@RequestBody News news) {
        QueryWrapper<News> queryWrapper = new QueryWrapper<News>();
        queryWrapper.eq("id",news.getId());
        return newsService.update(news,queryWrapper);
   }
}

application.properties

#配置数据源 druid  springboot默认的数据源 HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/newsdb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=root
#指定数据源的类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#日志的实现类
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

#已删除的值
mybatis-plus.global-config.db-config.logic-delete-value=1
#未删除的值
mybatis-plus.global-config.db-config.logic-not-delete-value=0

mybatis-plus.mapper-locations=classpath:com/yuan/mapper/*.xml
spring.application.name=yuan-biz
server.port=8082

启动类

@SpringBootApplication
@MapperScan("com.yuan.mapper")
public class YuanBizApplication {

    public static void main(String[] args) {
        SpringApplication.run(YuanBizApplication.class, args);
    }
}

yuan-gateway网关模块

依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--swagger start-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
        </dependency>
    </dependencies>

application.yaml

spring:
  application:
    name: yuan-gateway
  cloud:
    #注册到服务中心
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
    gateway:
       routes:
          #路由Id
          - id: yuan-biz
            #路由目标的服务名
            uri: http://localhost:8082/
            #路径匹配
            predicates:
              - Path=/news/**  #路径包含就转发,否则不转发
         #路由Id
          - id: yuan-login
            #路由目标的服务名
            uri: http://localhost:8081/
            #路径匹配
            predicates:
              - Path=/user/**  #路径包含就转发,否则不转发
server:
  port: 9999

CorsConfig

/**
 * 跨域允许
 */
@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true); // 允许cookies跨域
        config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
        config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
        config.addAllowedMethod("OPTIONS");// 允许提交请求的方法类型,*表示全部允许
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        config.addExposedHeader("token");
        config.addExposedHeader("Access-Control-Allow-Origin");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

CheckTokenFilter

@Slf4j
@Component
public class CheckTokenFilter implements GlobalFilter {
    /*
        ServerWebExchange命名为服务网络交换器,存放着重要的请求-响应属性、请求实例和响应实例等等
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取响应对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        //获取token
        String token = request.getHeaders().getFirst("token");
        log.info("========>CheckTokenFilter");
        //设置不需要验证token的请求
        List<String> excludeUrl = Arrays.asList(
                "/user/login",
                "/user/doLogin"
        );


        //获取请求路径
        String path = request.getPath().toString();

        log.info("path===>"+path);
        if(excludeUrl.contains(path)){
            //放行
            return chain.filter(exchange);
        }
        //验证token
       // String token = request.getHeaders().getFirst("token");
        log.info("token===>"+token);
        if(!StringUtils.hasText(token)){
            return getVoidMono(response, R.error().message("token为空"));
        }
        if(!JWTUtil.verify(token)){
            return getVoidMono(response,R.error().message("token不合法"));
        }
        //放行
        return chain.filter(exchange);
    }

    private Mono<Void> getVoidMono(ServerHttpResponse response,R r){
        response.getHeaders().add("Content-Type","application/json;charset=utf-8");
        String tokenEmpty = null;
        DataBuffer dataBuffer = null;
        try {
            tokenEmpty = new ObjectMapper().writeValueAsString(r);
            dataBuffer = response.bufferFactory().wrap(tokenEmpty.getBytes());
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return response.writeWith(Flux.just(dataBuffer));
    }
}

启动类

@SpringBootApplication
@EnableDiscoveryClient
public class YuanGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(YuanGatewayApplication.class, args);
    }
}

dto

/**
 * @author mo
 * @Description: Jwt工具类,用于生成和校验jwt
 */
public class JWTUtil {

    /**过期时间为半小时*/
    private static final long EXPRIE_TIME = 30*60*1000;
    /**加密时的密钥,不能泄露*/
    private static final String secret = "B8B7EDFB-53AC-2BD2-E804-461A0C961F29";

    /**
     * 校验用户名和密码
     * @param token
     * @param username
     * @return
     */
    public static boolean verify(String token) {
        try {
            // 加密方式必须与生成token时的加密方式一致
            Algorithm algorithm = Algorithm.HMAC512(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .build();
            verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    /**
     * 获取token中的信息无需secret解密也能获得
     * @param token
     * @return
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }


    /**
     * 生成JWTToken自定义 ItripUserDetails 用于封装用户信息
     * @param username
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String sign(String username) throws
            UnsupportedEncodingException {
        Date date = new Date(System.currentTimeMillis()+EXPRIE_TIME);
        Algorithm algorithm = Algorithm.HMAC512(secret);
        // 附带username信息
        return JWT.create()
                .withClaim("username", username)
                .withExpiresAt(date)
                .sign(algorithm);
    }

}

@Data
public class R {
    @ApiModelProperty(value = "是否成功标示,true:成功;false:失败")
    private String success;
    @ApiModelProperty(value = "异常码")
    private String errorCode;
    @ApiModelProperty(value = "响应提示信息")
    private String msg;
    @ApiModelProperty(value = "返回数据")
    private Object data;

    //把构造方法私有
    private R(){}

    //返回集合List  return R.ok().data("list",list)
    //登录成功  return R.ok().message("登录成功");
    //登录失败(密码错误) return R.error().code(10000).message("密码错误")  //链式编程

    //成功静态方法
    public static R ok(){
        R r = new R();
        r.setSuccess("true");
        r.setErrorCode(ResultCode.SUCCESS);
        r.setMsg("成功");
        return r;
    }

    //失败静态方法
    public static R error(){
        R r = new R();
        r.setSuccess("false");
        r.setErrorCode(ResultCode.ERROR);
        r.setMsg("失败");
        return r;
    }

    public R success(String success){
        this.setSuccess(success);
        return this;
    }

    public R message(String message){
        this.setMsg(message);
        return this;
    }

    public R code(String code){
        this.setErrorCode(code);
        return this;
    }

    public  R data(Object data){
        this.setData(data);
        return this;
    }

}
public interface ResultCode {

    public static String SUCCESS = "00000";  //成功
    public static String ERROR = "20001";  //失败
}

前端Vue

下载依赖

#安装cookies
npm install vue-cookies --save
#安装axios
npm install --save axios vue-axios
#安装elementUI
npm i element-ui -S
#安装路由
#npm install vue-router@3
npm install vue-router@3 or 4  

main.js

import Vue from 'vue'
import App from './App'
import router from "./router/index"
Vue.config.productionTip = false
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import axios from "axios";
import request from "./router/request"

//注册axios为全局的变量
Vue.prototype.$axios=axios;
Vue.use(ElementUI)
//导入拦截,注册未全部变量
Vue.prototype.$request=request;
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  request,
  components: { App },
  template: '<App/>'
})

App.vue

<template>
  <div id="app">
  <router-view></router-view>
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

router目录下index.js

import Vue from "vue"
import Router from "vue-router";
Vue.use(Router);

const router=new Router({
  routes:[
    {
      name:"Login",
      path:'/',
      component: ()=>import("@/components/Login")
     },
      {
          name:"Main",
          path: "/Main",
          component:()=>import("@/components/Main")
      }

      ]
});

//配置导航守卫
router.beforeEach((to, from,next) => {
  console.log("to======>"+to.path);
  console.log("from======>"+from.path);
  const isLogin = true;
  if(to.path!=="Login"){
    if(isLogin){
      next();
    }
    return false;
  }
  return next();
});
export default router;

request.js

import axios from "axios";
//导入cookie
import cookie from "js-cookie"
/*创建axios实例*/
const intance=axios.create({
  baseURL : "http://localhost:9999",
  timeout:10000
})

/*请求的拦截*/
intance.interceptors.request.use(function (config) {
  config.headers.token=cookie.get("token")
  return config;
},function (error) {
  console.log(error)
  return Promise.reject(error)
})

/*响应的拦截器*/
intance.interceptors.response.use(function (response) {
  const  res=response.data
  return  res;
},function (error) {
  return Promise.reject(error)
})

export default intance;

Login.vue

<template>
    <div style="width: 300px">
        <el-form ref="loginForm" :model="form" :rules="rules" label-width="80px" class="login-box">
            <h3 class="login-title">欢迎登录</h3>
            <el-form-item label="账号" prop="username">
                <el-input type="text" placeholder="请输入账号" v-model="form.username"/>
            </el-form-item>
            <el-form-item label="密码" prop="password">
                <el-input type="password" placeholder="请输入密码" v-model="form.password"/>
            </el-form-item>
            <el-form-item>
                <el-button type="primary" v-on:click="onsubmit('loginForm')">登录</el-button>
            </el-form-item>
        </el-form>
        <el-dialog title="温馨提示"  width="30%">
            <span>请输入账号和密码</span>
            <span slot="footer" class="dialog-footer">
          <el-button type="primary" @click="dialogVisible = false">确定</el-button>
        </span>
        </el-dialog>
    </div>
</template>

<script>
  import cookie from "js-cookie"
    export default {
        name: "Login",
        data(){
            return{
                form:{
                    username:'',
                    password:'',
                },
                //表单验证,需要在 el-form-item 元素中增加prop属性
                rules:{
                    username:[
                        {required:true,message:"账号不可为空",trigger:"blur"}
                    ],
                    password:[
                        {required:true,message:"密码不可为空",tigger:"blur"}
                    ]
                },

                //对话框显示和隐藏
                dialogVisible:false
            }
        },
        methods:{
            onsubmit(formName){
                //为表单绑定验证功能
                this.$refs[formName].validate((valid)=>{
                    if(valid){
                        this.$request.post("/user/doLogin?username="+this.form.username+"&password="+this.form.password)
                          .then((response)=>{
                            if (response!=="20001"){
                             // console.log(response.data.token)
                             // 第一个参数存储的token名,第二个参数是服务器返回的token值,第三个参数设置token的作用域
                             cookie.set("token",response.data.token,{admin:"localhost"})//设置token
                             console.log(cookie.get("token"))
                            this.$router.push('/Main');
                            }else {
                              alert(response.success)
                                alert("用户名或密码不正确")
                            }
                        })

                    }else{
                        this.dialogVisible=true;
                        return false;
                    }
                });
            }
        }
    }
</script>

<style scoped>

</style>

Main.vue

<template>
    <div>
        <h2>新闻列表</h2>
        <el-row class="newssavebtn">
            <el-button type="primary" @click="newsAdd()" icon="el-icon-plus" >新增</el-button>
        </el-row>
        <el-table
                :data="newsList"
                style="width: 100%">
            <el-table-column
                    prop="title"
                    label="标题"
                    width="180">
            </el-table-column>
            <el-table-column
                    prop="publisher"
                    label="发布人">
            </el-table-column>
            <el-table-column
                    prop="createTime"
                    label="发布时间">
            </el-table-column>
            <el-table-column label="操作">
                <!--template slot-scope="scope":存的是后台的数据-->
                <template slot-scope="scope">
                    <!--scope.$index:获取索引,
                       scope.row:获取所有数据
                       scope.row.id:获取id,如果实现删除功能就写  @click="handleDelete(scope.row.id)
                       -->
                    <el-button
                            size="mini"
                            @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
                    <el-button
                            size="mini"
                            type="danger"
                            @click="newsDelete(scope.row.id)">删除</el-button><!--scope.row.id:获取删除新闻id-->
                </template>
            </el-table-column>
        </el-table>
        <!--分页-->
        <el-pagination
                background
                layout="prev, pager, next"
                @current-change="handPage"
                :page-size="pageSize"
                :current-page="pageNum"
                :total="total">
        </el-pagination>
        <!--新增和修改弹窗-->
        <el-dialog
                title="提示"
                :visible.sync="dialogVisible"
                width="30%">
            <span>这是一段信息</span>
            <el-form ref="form" :model="news" label-width="80px">
                <el-form-item label="新闻标题">
                    <el-input v-model="news.title"></el-input>
                </el-form-item>
                <el-form-item label="新闻内容">
                    <el-input type="textarea" v-model="news.content"></el-input>
                </el-form-item>
                <el-form-item label="发布人">
                    <el-input v-model="news.publisher"></el-input>
                </el-form-item>
                <el-form-item>
                    <el-button type="primary" @click="onSubmit(news)" icon="el-icon-plus">提交</el-button>
                    <el-button>取消</el-button>
                </el-form-item>
            </el-form>
        </el-dialog>
    </div>
</template>

<script>
  import cookie from "js-cookie"
    export default {
        name: "Main",
        data(){
            return{
                newsList:[],
                dialogVisible:false,//控制弹窗显示和隐藏
                news:{},
                pageSize:5,
                pageNum:1,
                total:0
            }
        },
        methods:{
            //分页查询
            getNewsPage(){
              this.$request.post("/news/list?pageNum="+this.pageNum+"&pageSize="+this.pageSize+"").then((response)=>{
                   console.log(response+"\t"+this.pageNum+"\t"+this.pageSize)
                    //将查询的数据放到newsList中
                    this.newsList=response.records
                    this.total=response.total;

                })
                    .catch(function (error) {
                        console.log("=====>"+error)
                    })
            },
            //当前页面改变重新查数据 点击上一页,下一页
            handPage(pageNum){
                this.pageNum=pageNum
                this.getNewsPage()
            },
            //处理新增
            newsAdd(){
                this.dialogVisible = true;
            },
            //新增新闻
            newsAdds(){
                this.$request.post('/news/add', this.news)
                    .then((response)=> {
                        if (response===true){
                            this.$message({
                                message: '新增成功',
                                type: 'success'
                            })

                        }
                    })
            },
            //查询要修改的数据,从前端的row中获取
            handleEdit($index,row){
                this.dialogVisible=true
                this.news = row
            },

            // 定义提交事件判断是新增还是修改
            onSubmit:function (news) {
                if (news.id!=null){
                    this.newsUpdate();
                    this.news={};

                }else {
                    this.newsAdds();
                    this.news={};
                    this.dialogVisible = false
                }
            },
            //修改方法
            newsUpdate(){
                this.$request.post('/news/update', this.news)
                    .then((response)=> {
                        if (response===true){
                            this.$message({
                                message: '修改成功',
                                type: 'success'
                            })
                            this.getNewsPage()
                        }
                    })
                //隐藏模态框
                this.dialogVisible={};
                this.dialogVisible = false

            },
            //删除操作
            newsDelete(id){
                this.$confirm('此操作将永久删除该文件, 是否继续?', '提示',{
                    confirmButtonText: '确定',
                    cancelButtonText: '取消',
                    type: 'warning'
                }).then(() => {
                    this.$request.post("/news/delete?id="+id+"")
                        .then((res)=>{
                            if (res===true){
                                this.$message({
                                    message: '删除成功',
                                    type: 'success'
                                })
                                this.getNewsPage()
                            }else {
                                this.$message({
                                    message: '删除失败',
                                    type: 'success'
                                })
                            }
                        })
                }).catch(() => {
                    this.$message({
                        type: 'info',
                        message: '已取消删除'
                    });
                });
            }
        },
        mounted() {
            //默认时展示数据
            //this.getNewsList();
            //分页数据展示
            this.getNewsPage()
            //隐藏模态框
            this.dialogVisible=false;
        }
    }
</script>

<style scoped>

</style>
avatar

yuanyourdomain

写代码,做研究,记录生活。

RECOMMENDED

MyBatis 动态 SQL

2026-07-08

Nginx 基础入门

2026-07-08

Maven 多模块与私服

2026-07-08