Spring boot Security 用于权限管理,用户添加等。

1:添加依赖:

        <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-springsecurity4</artifactId>
    </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

2:创建验证service集成UserDetailService

    

package com.qingwenwei.security;

import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.qingwenwei.persistence.model.User;
import com.qingwenwei.service.UserService;
import org.springframework.stereotype.Service;

//授权认证
@Service
public class MyUserDetailsService implements UserDetailsService{ Logger logger = LogManager.getLogger(MyUserDetailsService.class); @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { logger.debug("得到用户"); User user = this.userService.findByUsername(username); //加密之后的密码。才能进行下面的授权认证。 logger.debug(user.getUsername()+"密码"+user.getPassword()); //加密之后的密码。 if(null == user) { throw new UsernameNotFoundException("Can't find user by username: " + username); } List<SimpleGrantedAuthority> grantedAuthorities = new ArrayList<>(); // grant roles to user for (String role : user.getRolesSet()) { logger.debug(role); grantedAuthorities.add(new SimpleGrantedAuthority(role)); } // user.setGrantedAuthorities(authorities); //用于登录时 @AuthenticationPrincipal 标签取值 return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorities); } }

3:进行配置

   

package com.qingwenwei.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    MyUserDetailsService myUserDetailsService;
//    @Bean
//    UserDetailsService myUserDetailsService() { // register userDetailsService
//        return new MyUserDetailsService();
//    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
//        authenticationProvider.setUserDetailsService(this.myUserDetailsService());
        authenticationProvider.setUserDetailsService(myUserDetailsService);
        authenticationProvider.setPasswordEncoder(this.bCryptPasswordEncoder());
        return authenticationProvider;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {  //全局配置
//        auth.inMemoryAuthentication()  //內存中存在的验证
//                .withUser("t").password("t").roles("USER")
//                .and()
//                .withUser("admin").password("admin").roles("ADMIN");

        auth.userDetailsService(myUserDetailsService).passwordEncoder(this.bCryptPasswordEncoder());    
//        auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
//        auth.authenticationProvider(this.authenticationProvider()); // different approach
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/user/settings").authenticated() // order matters
                .antMatchers("/", "/js/**", "/css/**","/avatar/**", "/images/**", "/fonts/**", "/bootstrap-select/**", "/bootstrap-datetimepicker/**", "/custom/**", "/daterangepicker/**", "/chartjs/**").permitAll() // these paths are configure not to require any authentication
                .antMatchers("/post/**").permitAll() // all posts are allowed to be viewed without authentication
                .antMatchers("/user/**").permitAll() // all user profiles are allowed to be viewed without authentication
                .antMatchers("/category/**").permitAll() // all categories are allowed to be viewed without authentication
                .antMatchers("/user/registration").permitAll()
                .antMatchers("/avatar/**").permitAll() // temp
                .antMatchers("/avatar1/**").permitAll() // temp
            .anyRequest().authenticated() // every request requires the user to be authenticated
                    .and()
            .formLogin()
                .loginPage("/user/login")
                .permitAll() // login URL can be accessed by anyone
                .and()
            .logout()
                .invalidateHttpSession(true)
                .clearAuthentication(true)
                .logoutSuccessUrl("/?logout")
                .permitAll();
    }
}

4:userController.java

    

package com.qingwenwei.web.controller;

import com.qingwenwei.exception.BadRequestException;
import com.qingwenwei.exception.ResourceNotFoundException;
import com.qingwenwei.persistence.model.User;
import com.qingwenwei.service.UserService;
import com.qingwenwei.util.NewUserFormValidator;
import com.qingwenwei.web.dto.UserRegistrationDto;
import com.qingwenwei.web.dto.UserSettingsDto;
import org.apache.catalina.servlet4preview.http.HttpServletRequest;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Map;

@Controller
public class UserController {

    Logger logger = LogManager.getLogger(UserController.class);
//    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private NewUserFormValidator userValidator;
    
    @RequestMapping(value = "/user/{userId}", method = RequestMethod.GET)
    public String showUserProfilePage(@RequestParam(value = "tab", required = false) String tabType, 
            @PathVariable Long userId, Model model) {
        if (null == userId) {
            throw new BadRequestException("Path variable userId cound not be null.");
        }
        Map<String, Object> attributes = this.userService.getUserProfileAndPostsByUserIdByTabType(userId, tabType);
        if (null == attributes) {
            throw new ResourceNotFoundException("attributes not found.");
        }
        model.addAllAttributes(attributes);
        return "forum/user-profile";
    }
    
    @RequestMapping(value = "/user/registration", method = RequestMethod.GET)
    public String showRegistrationPage(Model model) {
        model.addAttribute("userDto", new UserRegistrationDto());
        return "forum/user-registration";  //注册页面
    }
    
    @RequestMapping(value = "/user/registration", method = RequestMethod.POST)  //提交注册
    public String registerNewUser(@Valid @ModelAttribute("userDto") UserRegistrationDto userDto,
            BindingResult bindingResult, Model model, HttpServletRequest request) {
        /*
         * form validation, check username and email uniqueness
         */
        this.userValidator.validate(userDto, bindingResult);
        if (bindingResult.hasErrors()) {
                logger.info("BindingResult has errors >> " + bindingResult.getFieldError());
                return "forum/user-registration";
        }
        logger.debug("注册"+userDto.getMatchingPassword());
        Map<String, Object> attributes = this.userService.registerUserAccount(userDto, request);
        if (null == attributes) {
            throw new ResourceNotFoundException("attributes not found.");
        }
        model.addAllAttributes(attributes);
        return "forum/user-registration-result";
    }
    
    @RequestMapping(value = "/user/login", method = RequestMethod.GET)
    public String displayLoginPage(Model model) {
        logger.debug("user/login登录");
        model.addAttribute("title", "用户登陆");
        return "forum/user-login"; //登录界面,验证没通过。
    }
    
    @RequestMapping(value = "/user/login-success", method = RequestMethod.GET)
    public String showAdminPage() {
        logger.debug("登录成功");
        return "forum/user-login";
//        return "/";
    }
    
    @RequestMapping(value = "/confirm", method = RequestMethod.GET)
    public String confirmRegistration(@RequestParam("token") String token) {
        return "forum/confirmation";
    }
    
    @RequestMapping(value = "/confirm", method = RequestMethod.POST)
    public String processConfirmation() {
        return "forum/confirmation";
    }
    
    @RequestMapping(value = "/user/settings", method = RequestMethod.GET)
    public String showUserSettingsPage(Model model) {
        Map<String, Object> attributes = this.userService.getUserSettingPage();
        if (null == attributes) {
            throw new ResourceNotFoundException("attributes not found.");
        }
        model.addAllAttributes(attributes);
        return "forum/user-settings";
    }

    @RequestMapping(value = "/user/settings", method = RequestMethod.POST)
    public String handleFileUpload(@ModelAttribute("userSettingsDto") UserSettingsDto userSettingsDto, Model model) {
//        User byConfirmationToken = userService.findByConfirmationToken(userSettingsDto.getPasswordConfirmation());
//        logger.debug(byConfirmationToken.getPassword());
//        logger.debug(userSettingsDto.getPassword());

        if (null == userSettingsDto) {
            throw new BadRequestException("UserSettingsDto cound not be null.");
        }
        Map<String, Object> attributes = this.userService.updateUserProfile(userSettingsDto);
        if (null == attributes) {
            throw new ResourceNotFoundException("attributes not found.");
        }
        model.addAllAttributes(attributes);
        return "forum/user-settings";
    }

}

5:UserService.java UserServiceImpl.java

   

package com.qingwenwei.service;

import com.qingwenwei.persistence.model.User;
import com.qingwenwei.web.dto.UserRegistrationDto;
import com.qingwenwei.web.dto.UserSettingsDto;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public interface UserService {
    
    int save(User user);
    
    User findById(Long id);
    
    User findByUsername(String username);
    
    User findByEmail(String email);
    
    User findByConfirmationToken(String confirmationToken);
    
    User findAuthenticatedUser();
    
    Map<String, Object> getUserProfileAndPostsByUserIdByTabType(Long userId, String tabType);
    
    Map<String, Object> updateUserProfile(UserSettingsDto newUserSettingsForm);
    
    Map<String, Object> getUserSettingPage();
    
    Map<String, Object> registerUserAccount(UserRegistrationDto userDto, HttpServletRequest request);

}
package com.qingwenwei.service.impl;

import com.qingwenwei.event.OnRegistrationCompleteEvent;
import com.qingwenwei.persistence.dao.CommentMapper;
import com.qingwenwei.persistence.dao.PostMapper;
import com.qingwenwei.persistence.dao.UserMapper;
import com.qingwenwei.persistence.model.Comment;
import com.qingwenwei.persistence.model.Post;
import com.qingwenwei.persistence.model.User;
import com.qingwenwei.service.StorageService;
import com.qingwenwei.service.UserService;
import com.qingwenwei.web.dto.UserRegistrationDto;
import com.qingwenwei.web.dto.UserSettingsDto;
import org.apache.logging.log4j.LogManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.sql.Timestamp;
import java.util.*;

@Service("userService")
public class UserServiceImpl implements UserService {

    //    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
    org.apache.logging.log4j.Logger logger = LogManager.getLogger(UserServiceImpl.class);
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private PostMapper postMapper;
    
    @Autowired
    private CommentMapper commentMapper;
    
//    @Autowired
//    private VerificationTokenMapper verificationTokenMapper;
    
    @Autowired
    private StorageService storageService;
    
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @Autowired
    private ApplicationEventPublisher evenPublisher;
    

    @Override
    public User findById(Long id) {
        return userMapper.findById(id);
    }

    @Override
    public User findByEmail(String email) {
        return userMapper.findByEmail(email);
    }

    @Override
    public User findByConfirmationToken(String confirmationToken) {
        return userMapper.findByConfirmationToken(confirmationToken);
    }

    @Override
    public User findByUsername(String username) {
        return userMapper.findByUsername(username);
    }
    
    @Override  //重置密码
    public int save(User user) {
        user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));  //保存时应该将密码编码
        return userMapper.save(user);
    }
//    @Override
//    public int save(User user) {
//        user.setPassword(user.getPassword());
//        return userMapper.save(user);
//    }

    @Override
    public Map<String, Object> getUserProfileAndPostsByUserIdByTabType(Long userId, String tabType) {
        if (null == userId) {
            return null;
        }
        User user = this.userMapper.findById(userId);
        if (null == user) {
            return null;
        }
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("user", user);
        String activeTab = tabType == null ? "posts" : tabType;
        if ("posts".equalsIgnoreCase(activeTab)) {
            List<Post> posts = this.postMapper.findPostsByUserId(userId);
            attributes.put("posts", posts);
        } else if ("comments".equalsIgnoreCase(activeTab)) {
            List<Comment> comments = this.commentMapper.findCommentsByUserId(userId);
            attributes.put("comments", comments);
        }
        attributes.put("activeTab", activeTab);
        return attributes;
    }

    @Override
    public User findAuthenticatedUser() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        String username = auth.getName();
        return this.userMapper.findByUsername(username);
    }

    /**
     * 更新用户profile
     * @param userSettingsDto
     * @return
     */
    @Override
    public Map<String, Object> updateUserProfile(UserSettingsDto userSettingsDto) {
        Map<String, Object> attributes = new HashMap<>();
//        String authenticatedUsername = this.findAuthenticatedUser().getUsername();
        User authenticatedUser = this.findAuthenticatedUser();

        String authenticatedUsername = authenticatedUser.getUsername();
        String password = authenticatedUser.getPassword();
        logger.debug("passward"+password);
        if (null == authenticatedUsername || authenticatedUsername.equalsIgnoreCase("")
                || null == userSettingsDto
                || userSettingsDto.getEmail().isEmpty()
                || userSettingsDto.getEmail().equals("")) {
            attributes.put("uploadResultMessage", "uploadFailure");
            return attributes;
        }
        // update user profile
        User user = this.storageService.store(userSettingsDto.getAvatar(), authenticatedUsername);
        if (null == user) {
            attributes.put("uploadResultMessage", "uploadFailure");
            user = this.findAuthenticatedUser(); // find authenticated user if no user found
        }
        user.setPassword(password);
        user.setEmail(userSettingsDto.getEmail());
        user.setBio(userSettingsDto.getBio());

        this.userMapper.update(user);
        
        // return attributes
        attributes.put("user", user);
        attributes.put("uploadResultMessage", "uploadSuccess");
        return attributes;
    }

    @Override
    public Map<String, Object> getUserSettingPage() {
        User user = this.findAuthenticatedUser();
        UserSettingsDto newUserSettingsForm = new UserSettingsDto();
        newUserSettingsForm.setBio(user.getBio());
        newUserSettingsForm.setEmail(user.getEmail());
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("user", user);
        attributes.put("userSettingsDto", newUserSettingsForm);
        return attributes;
    }

    @Override
    public Map<String, Object> registerUserAccount(UserRegistrationDto userDto, HttpServletRequest request) {
        Map<String, Object> attributes = new HashMap<>();
        
        // save newly registered user
        User user = new User();

        user.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword()));  //保存时应该将密码编码

//        user.setPassword(userDto.getPassword());
        user.setUsername(userDto.getUsername());
        user.setEmail(userDto.getEmail());
        user.setDateCreated(new Timestamp(System.currentTimeMillis()));
        user.activated(true);
        user.setRoles(User.USER);
        user.setConfirmationToken(UUID.randomUUID().toString());

        // save new user and get number of affected row
        logger.debug("用户注册");
        int affectedRow = userMapper.save(user);
        logger.debug("用户注册成功");
        // publish registration event
        String appUrl = "http://" + request.getServerName() + ":" + request.getServerPort();
        Locale locale = request.getLocale();
        OnRegistrationCompleteEvent event = new OnRegistrationCompleteEvent(user.getUsername(), appUrl, locale);
        this.evenPublisher.publishEvent(event);
        
        // populate attributes
        String registrationResult = affectedRow == 1 ? "success" : "failure";
        attributes.put("userRegistrationResult", registrationResult);
        return attributes;
    }

}

6:UserMapper.java

    

package com.qingwenwei.persistence.dao;

import com.qingwenwei.persistence.model.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserMapper {
    
    int save(@Param("user") User user);
    
    int update(@Param("user") User user);
    
    List<User> findAll();
    
    User findById(Long id);
    
    User findByUsername(String username);
    
    User findByEmail(String email);
    
    User findByConfirmationToken(String confirmationToken);
    
}

7;UserMapper.xml

   

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qingwenwei.persistence.dao.UserMapper">
    
    <resultMap id="UserResultMap" type="com.qingwenwei.persistence.model.User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="password" column="password"/>
        <result property="email" column="email"/>
        <result property="confirmationToken" column="confirmation_token"/>
        <result property="activated" column="activated"/>
        <result property="dateCreated" column="date_created"/>
        <result property="avatarLocation" column="avatar_location"/>
        <result property="bio" column="bio"/>
    </resultMap>
    
    <sql id="baseColumns" >
        id, username, password, email, activated, date_created, avatar_location, bio,roles
    </sql>
    <insert id="save" parameterType="com.qingwenwei.persistence.model.User">
        INSERT INTO `T_USER`
            (
            username,
            password,
            email,
            confirmation_token,
            activated,
            date_created,
            avatar_location,
            bio,
            roles
            )
        VALUES 
            (
            #{user.username},
            #{user.password},
            #{user.email},
            #{user.confirmationToken},
            #{user.activated},
            #{user.dateCreated},
            #{user.avatarLocation},
            #{user.bio},
            #{user.roles}
            )
    </insert>
    
    <select id="findById" parameterType="Long" resultMap="UserResultMap">
        SELECT 
            id,
            username, 
            password, 
            email, 
            activated, 
            date_created, 
            avatar_location, 
            bio,
            roles
        FROM T_USER
        WHERE id = #{id}
    </select>
    
    <select id="findByUsername" parameterType="String" resultMap="UserResultMap">
        SELECT 
            id,
            username, 
            password, 
            email, 
            activated, 
            date_created, 
            avatar_location, 
            bio,
            roles
        FROM T_USER
        WHERE username = #{username}
    </select>
    
    <select id="findByEmail" parameterType="String" resultMap="UserResultMap">
        SELECT
            id,
            username, 
            password, 
            email, 
            activated, 
            date_created, 
            avatar_location, 
            bio,
            roles
        FROM T_USER
        WHERE email = #{email}
    </select>
    
    <select id="findByConfirmationToken" parameterType="String" resultMap="UserResultMap">
        SELECT 
            id,
            username, 
            password, 
            email, 
            activated, 
            date_created, 
            avatar_location, 
            bio,
            confirmation_token,
            roles
        FROM T_USER
        WHERE confirmation_token = #{confirmationToken}
    </select>
    
    <select id="findAll" resultMap="UserResultMap">
        SELECT 
            id,
            username, 
            password, 
            email, 
            activated, 
            date_created, 
            avatar_location, 
            bio,
            confirmation_token,
            roles
        FROM T_USER
    </select>
    
    <update id="update" parameterType="com.qingwenwei.persistence.model.User">
        UPDATE T_USER SET 
            password = #{user.password},
            email = #{user.email},
            date_created = #{user.dateCreated},
            avatar_location = #{user.avatarLocation},
            bio = #{user.bio}
        WHERE id = #{user.id}
    </update>    
    
</mapper>

8;User.java

    

package com.qingwenwei.persistence.model;

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

public class User implements Serializable {

    private static final long serialVersionUID = 1L;
    
    // constants
    public static String USER = "USER";  //两种用户
    public static String ADMIN = "ADMIN";
    
    private Long id;
    private String username;
    private String password;
    private String email;
    private String confirmationToken;
    private Long activated;
    private Timestamp dateCreated;
    private String avatarLocation;
    private String bio;
    private String roles;
    
    public User() {
        
    }
    
    public Long getId() {
        return id;
    }
    
    public void setId(Long id) {
        this.id = id;
    }
    
    public String getUsername() {
        return username;
    }
    
    public void setUsername(String userName) {
        this.username = userName;
    }
    
    public String getPassword() {
        return password;
    }
    
    public void setPassword(String password) {
        this.password = password;
    }
    
    public String getEmail() {
        return email;
    }
    
    public void setEmail(String email) {
        this.email = email;
    }
    
    public String getConfirmationToken() {
        return confirmationToken;
    }

    public void setConfirmationToken(String confirmationToken) {
        this.confirmationToken = confirmationToken;
    }

    public Long getActivated() {
        return activated;
    }

    public void setActivated(Long activated) {
        this.activated = activated;
    }

    public static void setUSER(String USER) {
        User.USER = USER;
    }

    public static void setADMIN(String ADMIN) {
        User.ADMIN = ADMIN;
    }

    public static long getSerialVersionUID() {
        return serialVersionUID;

    }

    public static String getUSER() {
        return USER;
    }

    public static String getADMIN() {
        return ADMIN;
    }

    public Timestamp getDateCreated() {
        return dateCreated;
    }

    public void setDateCreated(Timestamp dateCreated) {
        this.dateCreated = dateCreated;
    }

    public String getAvatarLocation() {
        return avatarLocation;
    }

    public void setAvatarLocation(String avatarLocation) {
        this.avatarLocation = avatarLocation;
    }

    public String getBio() {
        return bio;
    }

    public void setBio(String bio) {
        this.bio = bio;
    }
    
    public String getRoles() {
        return roles;
    }

    public void setRoles(String roles) {
        this.roles = roles;
    }  //赋予该用户的角色
    
    public Set<String> getRolesSet() {
        if (null == roles) {
            return null;
        }
        return Collections.unmodifiableSet(
                new HashSet<String>(Arrays.asList(getRoles().split(","))));
    }
    
    public void addRole(String role) {
        String currRoles = this.getRoles();
        if (null == currRoles || this.getRoles().contains(role)) {
            return;
        }
        this.setRoles(currRoles + "," + role);
    }
    
    public void activated(boolean activated) {
        this.setActivated(activated == true ? 1L : 0L);
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email
                + ", confirmationToken=" + confirmationToken + ", activated=" + activated + ", dateCreated="
                + dateCreated + ", avatarLocation=" + avatarLocation + ", bio=" + bio + ", roles=" + roles + "]";
    }

}

9:配置成功。

github:https://github.com/1367356/springBootForum

原文地址:https://www.cnblogs.com/liyafei/p/8735173.html