MyBlog background development

Java back end interface and Vue front end

1. See the effect first


publish an article

Background login

Blog details

Java back end interface development:
Using springboot+mybaitsPlus+shiro+jwt+lombok+redis; database mysql5.7+
Here, we use IDEA to develop the background interface. It's easy to create a new interface

2. Create the shpringboot project

Project structure:

pom.xml

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

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

3. Integrating MyBtisPlus

1. Introduce related dependency

<!--mp-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.2.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

<!--mp Code generator -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.2.0</version>
</dependency>

2. Write configuration file

#port
server:
  port: 8081

#Database related
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
mybatis-plus:
  type-aliases-package: com.hu.my_web_site.entity
  mapper-locations: classpath*:/mapper/**.xml

3. Write GenerateCode to generate code automatically

// For example, execute the main method, the console input module table name, and press enter to automatically generate the corresponding project directory
public class CodeGenerator {

    /**
     * <p>
     * Read console content
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("Please enter" + tip + ": ");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotEmpty(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("Please enter the correct" + tip + "!");
    }

    public static void main(String[] args) {
        // Code generator 
        AutoGenerator mpg = new AutoGenerator();

        // Global configuration
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
//        gc.setOutputDir("D:\\test");
        gc.setAuthor("hucong");
        gc.setOpen(false);
        // gc.setSwagger2(true); entity attribute Swagger2 annotation
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);

        // Data source configuration
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=UTC");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        mpg.setDataSource(dsc);

        // Package configuration
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(null);
        pc.setParent("com.hu.my_web_site");
        mpg.setPackageInfo(pc);

        // Custom configuration
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // If the template engine is freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // If the template engine is velocity
        // String templatePath = "/templates/mapper.xml.vm";

        // Custom output configuration
        List<FileOutConfig> focList = new ArrayList<>();
        // Custom configuration will be output first
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // Customize the output file name. If you set the prefix and suffix for Entity, please note that the name of xml will change accordingly!!
                return projectPath + "/src/main/resources/mapper/"
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // Configure templates
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // Policy configuration
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude(scanner("Table name, multiple comma separated").split(","));
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("m_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}

4. Database related sql. Here are two tables. Log in to the registry and save the Blog article table

DROP TABLE IF EXISTS `m_blog`;
CREATE TABLE `m_blog`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `created` datetime(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0),
  `status` tinyint(4) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 34 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

CREATE TABLE `m_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `avatar` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `status` int(5) NOT NULL,
  `created` datetime(0) NULL DEFAULT NULL,
  `last_login` datetime(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `UK_USERNAME`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `m_user` VALUES (1, 'hucong', ''http://www.16sucai.com/2016/05/82507.html'', '1069997178@qq.com', '96e79218965eb72c92a549dd5a330112', 0, '2020-04-20 10:44:01', NULL);

After construction, the structure of the project is as shown in the figure above

Test interface (that is to test whether it is connected to the database) I will not write, certainly right!!! ha-ha

mybatisplus integration completed

4. Unified result encapsulation

Result class, which is used to encapsulate the results returned asynchronously and uniformly. Generally speaking, there are several essential elements in the result (encapsulated in the common package)
Whether it is successful or not can be represented by code (for example, 200 indicates success, 400 indicates exception)
Result message
Result data

@Data
public class Result implements Serializable {
    private int code; // 200 is normal, non 200 indicates abnormal
    private String msg;
    private Object data;
    public static Result succ(Object data) {
        return succ(200, "Operation successful", 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;
    }

}

5. Integrate shiro+jwt to realize session sharing

Basic ideas:


1 * *. Import pom dependency**

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis-spring-boot-starter</artifactId>
    <version>3.2.1</version>
</dependency>

<!-- hutool Tools-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.3</version>
</dependency>

<!-- jwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2. Write yml

#shiro-redis
shiro-redis:
  enabled: true
  redis-manager:
    host: 127.0.0.1:6379

3. Write the configuration class ShiroConfig (put it under the config package)

@Configuration
public class ShiroConfig {

    @Autowired
    JwtFilter jwtFilter;

    @Bean
    public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();

        // inject redisSessionDAO
        sessionManager.setSessionDAO(redisSessionDAO);
        return sessionManager;
    }

    @Bean
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm,
                                                     SessionManager sessionManager,
                                                     RedisCacheManager redisCacheManager) {

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(accountRealm);

        //inject sessionManager
        securityManager.setSessionManager(sessionManager);

        // inject redisCacheManager
        securityManager.setCacheManager(redisCacheManager);
        return securityManager;
    }

    /**
     * Define filter chain
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        Map<String, String> filterMap = new LinkedHashMap<>();

        filterMap.put("/**", "jwt");
        chainDefinition.addPathDefinitions(filterMap);
        return chainDefinition;
    }

    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
                                                         ShiroFilterChainDefinition shiroFilterChainDefinition) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);

        Map<String, Filter> filters = new HashMap<>();
        filters.put("jwt", jwtFilter);
        shiroFilter.setFilters(filters);

        Map<String, String> filterMap = shiroFilterChainDefinition.getFilterChainMap();

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

}

1. Introduce RedisSessionDAO and RedisCacheManager to solve the problem that the permission data and session information of shiro can be saved in redis to realize session sharing.
2. The session manager and DefaultWebSecurityManager are rewritten. At the same time, in order to turn off the session mode of shiro, it needs to be set to false in DefaultWebSecurityManager, so that users can no longer log in to shiro through the session mode. You'll log in with jwt credentials later.

3. In ShiroFilterChainDefinition, instead of blocking the Controller access path by encoding, all routes need to pass through the JwtFilter filter, and then judge whether the request header contains jwt information. Log in if you have, or skip if you don't. After skipping, there is a shiro annotation in the Controller to intercept again, such as @ requireauthentication, to control permission access. So, next, account Realm (custom Realm) appears in ShiroConfig, and JwtFilter (filter).

4.AccountRealm (under shiro package)

AccountRealm is the logic of shiro's login or permission verification, which is the core,

Three methods need to be overridden, namely
supports: in order for the realm to support jwt's credential verification
doGetAuthorizationInfo: permission verification
doGetAuthenticationInfo: login authentication verification

@Component
public class AccountRealm extends AuthorizingRealm {

    @Autowired
    JwtUtils jwtUtils;

    @Autowired
    UserService userService;

    /**
     * In order to make the realm support jwt's certificate verification
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * to grant authorization
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     * authentication
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        JwtToken jwtToken = (JwtToken) token;

        String userId = jwtUtils.getClaimByToken((String) jwtToken.getPrincipal()).getSubject();

        User user = userService.getById(Long.valueOf(userId));
        if (user == null) {
            throw new UnknownAccountException("Account does not exist");
        }

        if (user.getStatus() == -1) {
            throw new LockedAccountException("Account locked");
        }

        AccountProfile profile = new AccountProfile();
        BeanUtil.copyProperties(user, profile);

        return new SimpleAuthenticationInfo(profile, jwtToken.getCredentials(), getName());
    }
}

In fact, it's mainly the method of doGetAuthenticationInfo login authentication. You can see that we get the user information through jwt, judge the user's status, and finally throw the corresponding exception information. Otherwise, we encapsulate it as SimpleAuthenticationInfo and return it to shiro.
Next, we will gradually analyze the new classes: 1. The default support of shiro is UsernamePasswordToken, and now we use the jwt method, so here we customize a JwtToken to complete the support method of shiro.

5.JwtToken (under shiro package)
1.jwttken:

public class JwtToken implements AuthenticationToken {
    //shiro's default support is UsernamePasswordToken,
    // Now we use the jwt method, so here we customize a JwtToken,
    // To complete shiro's supports method
    private String token;
    public JwtToken(String jwt) {
        this.token = jwt;
    }
    @Override
    public Object getPrincipal() {
        return token;
    }
    @Override
    public Object getCredentials() {
        return token;
    }
}

2. jwt utils is a tool class for generating and verifying jwt. Some jwt related key information is configured from the project configuration file: (under the utils package)

/**
 * jwt Tools
 */
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "hu.jwt")
public class JwtUtils {
    private String secret;
    private long expire;
    private String header;
    /**
     * Generate jwt token
     */
    public String generateToken(long userId) {
        Date nowDate = new Date();
        //Expiration time
        Date expireDate = new Date(nowDate.getTime() + expire * 1000);
        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(userId+"")
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e){
            log.debug("validate is token error ", e);
            return null;
        }
    }
    /**
     * token Expired or not
     * @return  true: be overdue
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }
}

3. In AccountRealm, we also use AccountProfile, which is a carrier of user information returned after successful login,

@Data
public class AccountProfile implements Serializable {
    //AccountProfile is a carrier of user information returned after successful login,
    private Long id;
    private String username;
    private String avatar;
    private String email;
}

ok, after the basic verification route is completed, a small amount of basic information configuration is required:

hu:
  jwt:
    #
    secret: f4e2e52034348f86b67cde581c0f9eb5
    # token
    expire: 604800
    header: Authorization

6.JwtFilter (under shiro package)
This filter is our focus. Here, we inherit Shiro's built-in AuthenticatingFilter, a filter that can be built-in to automatically log in methods, and also inherit BasicHttpAuthenticationFilter.

Several methods need to be overridden:

createToken: to implement login, I need to generate jwttoken supported by my custom

Access denied: intercept verification. When the header does not have Authorization, it passes directly and does not need to log in automatically. When it does, first verify the validity of jwt, and execute the executeLogin method directly to realize automatic login if there is no problem

onLoginFailure: the method entered when logging in an exception, encapsulating the exception information directly and throwing

preHandle: the pre interception of interceptors, because this is a front-end and back-end separation project. In addition to cross domain global configuration, cross domain support is also required in interceptors. In this way, the interceptor will not be restricted before entering the Controller.

@Component
public class JwtFilter extends AuthenticatingFilter {

    @Autowired
    JwtUtils jwtUtils;

    //To log in, we need to generate our own JwtToken
    @Override
    protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if(StringUtils.isEmpty(jwt)) {
            return null;
        }
        return new JwtToken(jwt);
    }
    /**
     * Intercept verification: when there is no Authorization in the header, we directly pass it without automatic login;
     * When there is, first of all, we verify the effectiveness of jwt. If there is no problem, we will directly execute the executeLogin method to realize automatic login
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        String jwt = request.getHeader("Authorization");
        if(StringUtils.isEmpty(jwt)) {
            return true;
        } else {

            // Verify jwt
            Claims claim = jwtUtils.getClaimByToken(jwt);
            if(claim == null || jwtUtils.isTokenExpired(claim.getExpiration())) {
                throw new ExpiredCredentialsException("token Invalid, please login again");
            }

            // Perform login
            return executeLogin(servletRequest, servletResponse);
        }
    }
    /**
     * When logging in an exception, we directly encapsulate the exception information and throw
     * @param token
     * @param e
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {

        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        Throwable throwable = e.getCause() == null ? e : e.getCause();
        Result result = Result.fail(throwable.getMessage());
        String json = JSONUtil.toJsonStr(result);

        try {
            httpServletResponse.getWriter().print(json);
        } catch (IOException ioException) {

        }
        return false;
    }

    /**
     * The pre interception of interceptors is because we are a front-end and back-end analysis project. In addition to cross domain global configuration, the,
     * We also need to provide cross domain support in interceptors. In this way, the interceptor will not be restricted before entering the Controller.
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @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"));
        // When cross domain, an OPTIONS request will be sent first. Here, we directly return the OPTIONS request to the normal state
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());
            return false;
        }

        return super.preHandle(request, response);
    }
}

shiro has been integrated and used jwt for authentication.

6 global exception handling

Location: (package com.hu.my_web_site.common.exception ;)

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * @ControllerAdvice Represents the definition of global controller exception handling,
     * @ExceptionHandler Indicates targeted exception handling. Each exception can be targeted.
     */

    /**
     * // Catch shiro's exception
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(value = ShiroException.class)
    public Result handler(ShiroException e) {
        log.error("Runtime exception:----------------{}", e);
        return Result.fail(401, e.getMessage(), null);
    }

    /**
     * @Validated Check exception error
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Result handler(MethodArgumentNotValidException e) {
        log.error("Entity verification exception:----------------{}", e);
        BindingResult bindingResult = e.getBindingResult();
        ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();

        return Result.fail(objectError.getDefaultMessage());
    }
    /**
     * Handle the exception of Assert
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = IllegalArgumentException.class)
    public Result handler(IllegalArgumentException e) {
        log.error("Assert Exception:----------------{}", e);
        return Result.fail(e.getMessage());
    }

    /**
     * RuntimeException: Catch other exceptions
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = RuntimeException.class)
    public Result handler(RuntimeException e) {
        log.error("Runtime exception:----------------{}", e);
        return Result.fail(e.getMessage());
    }

}

7 verification entity class

user entity class

@TableId(value = "id", type = IdType.AUTO)
private Long id;

@NotBlank( message="Nickname cannot be empty")
private String username;

private String avatar;

@NotBlank(message = "Mailbox cannot be empty")
@Email(message = "Incorrect mailbox format")
private String email;

blog entity class

//Inspection Time 
@JsonFormat(pattern = "yyy-MM-dd")
private LocalDateTime created;

8. cross domain problems

1. It's very simple. It only needs to be on the controller
Add @ CrossOrigin

2. You can also write global cross domain in config package (I don't like to write so many codes, so I used the last annotation method)

/**
 * Solve cross domain problems
 */
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

9. Login interface development

@RestController
@CrossOrigin
public class loginController {

    @Autowired
    UserService userService;

    @Autowired
    JwtUtils jwtUtils;

    @PostMapping("/login")
    public Result login(@Validated @RequestBody loginDto loginDto, HttpServletResponse response) {

        User user = userService.getOne(new QueryWrapper<User>().eq("username", loginDto.getUsername()));
        Assert.notNull(user, "The user name does not exist or the password is wrong");

        if(!user.getPassword().equals(SecureUtil.md5(loginDto.getPassword()))){
            return Result.fail("Incorrect password");
        }
        String jwt = jwtUtils.generateToken(user.getId());

        response.setHeader("Authorization", jwt);
        response.setHeader("Access-control-Expose-Headers", "Authorization");

        return Result.succ(MapUtil.builder()
                .put("id", user.getId())
                .put("username", user.getUsername())
                .put("avatar", user.getAvatar())
                .put("email", user.getEmail())
                .map()
        );
    }

    @RequiresAuthentication
    @GetMapping("/logout")
    public Result logout() {
        SecurityUtils.getSubject().logout();
        return Result.succ(null);
    }
}

10. Development of blog interface

@RestController
@CrossOrigin
public class BlogController {

    @Autowired
    private BlogService blogService;


    //paging operation 
    @GetMapping("/blogs")
    public Result list(@RequestParam(defaultValue = "1") Integer currentPage){
        Page page = new Page(currentPage,5);
        IPage pageData = blogService.page(page, new QueryWrapper<Blog>().orderByDesc("created"));
        return Result.succ(pageData);
    }

    //Query a record by id
    @GetMapping("/blog/{id}")
    public Result detail(@PathVariable Long id){
        Blog blog = blogService.getById(id);
        //Judge whether it is empty
        Assert.notNull(blog,"The content is empty");
        return Result.succ(blog);

    }

    //Modify or add
    @PutMapping("/blog/edit")
    public Result edit(@Validated @RequestBody Blog blog){
        Blog temp;
        if (blog.getId() != null){
            //edit
            //Get blog details through id
            temp= blogService.getById(blog.getId());
            // You can only edit your own articles
            System.out.println(((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId());
            Assert.isTrue(temp.getUserId().longValue() == ((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId(), "No permission to edit");

        }else{
            //add to
            temp = new Blog();
            temp.setUserId(((AccountProfile) SecurityUtils.getSubject().getPrincipal()).getId());
            temp.setCreated(LocalDateTime.now());
            temp.setStatus(0);
        }
        BeanUtil.copyProperties(blog, temp, "id", "userId", "created", "status");
        blogService.saveOrUpdate(temp);
        return Result.succ(null);
    }



    //Delete a record by id
    @GetMapping("/delete/{id}")
    private Result delete(@PathVariable("id") Long id){
        boolean remove = blogService.removeById(id);
        Assert.notNull(remove,"Delete succeeded");
        return  Result.succ(remove);
    }

}


Background interface complete

Keywords: Shiro Session Redis Spring

Added by sergio-pro on Sat, 20 Jun 2020 10:11:20 +0300