Demo material from niuke network

Note: SpringBoot2.3.x large version is best to avoid bugs in the way!

1. Build a Demo environment

1.1 Database SQL

1.1.1 Creating Data

Create a new database named Community with the encoding type UTF8MB4

1.1.2 the user table

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `salt` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `email` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `type` int(11) NULL DEFAULT NULL COMMENT '0- Common user; 1- Super administrator; 2 - the moderator; ',
  `status` int(11) NULL DEFAULT NULL COMMENT '0- inactive; 1- Active; ',
  `activation_code` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `header_url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL.PRIMARY KEY (`id`) USING BTREE,
  INDEX `index_username`(`username`(20)) USING BTREE,
  INDEX `index_email`(`email`(20)) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 156 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;
Copy the code

1.2 Initial project import

Download the initial project and import it using IDEA! (If the link doesn’t work, please leave a comment and I’ll update you!)

  • Link: pan.baidu.com/s/1CQ9rDbpo…
  • Extract the code:3gyt

The Demo project structure is as follows:

2. SpringBoot integrates Spring Security

2.1 Pom.xml introduces dependencies

<! -- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.3.1. RELEASE</version>
</dependency>
Copy the code

We can go to the Maven remote repository to search:

SpringSecurity takes effect immediately when we introduce dependencies! Start the project after the main function, we visit to test under the home page: http://localhost:8080/community/index, then it will automatically be intercepted deng SpringSecurity own login page:

The default login page provided by SpringSecurity is user, and the password is printed to the console as a log when we start the main function of the project:

After logging in with your account and password, you can enter inedx.html, the home page of our project

Q: So how to use your own custom login page and your own data in the user name and password to control login permission? Then take this question step by step and go down!

2.2 Add work to the User entity

We use annotations from the Lombok plug-in to simplify setters/getters as well as constructors and toString methods, and have the added benefit of showing more clearly what methods need to be overridden after implementing the UserDetails interface!

/ * * *@Auther: csp1999
 * @Date: 2020/12/02/17:29
 * @Description: * /
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ToString
public class User implements UserDetails {

    private int id;
    private String username;
    private String password;
    private String salt;
    private String email;
    private int type;
    private int status;
    private String activationCode;
    private String headerUrl;
    private Date createTime;

    /** * Gets the set of permissions that a user has **@return* /
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        ArrayList<GrantedAuthority> list = new ArrayList<>();
        // We only give the user one permission in demo
        list.add(new GrantedAuthority() {
            @Override
            public String getAuthority(a) {
                switch (type) {
                    case 1:
                        return "ADMIM";/ / administrator
                    default:
                        return "USER";// Common and user}}});return list;
    }

    /** * Returns true: the account has not expired@return* /
    @Override
    public boolean isAccountNonExpired(a) {
        return true;
    }

    /** * Returns true: the account is unlocked. ** Returns false: the account is locked@return* /
    @Override
    public boolean isAccountNonLocked(a) {
        return true;
    }

    /** * returns true: the certificate has not expired. ** Returns false: the certificate has expired@return* /
    @Override
    public boolean isCredentialsNonExpired(a) {
        return true;
    }

    /** * Returns true: the account is available. ** Returns false: the account is unavailable@return* /
    @Override
    public boolean isEnabled(a) {
        return true; }}Copy the code

2.3 Add work to the UserService implementation class

/ * * *@Auther: csp1999
 * @Date: 2020/12/02/17:31
 * @Description: * /
@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    /** * select * from user where username = 'username'@param username
     * @return* /
    public User findUserByName(String username) {
        return userMapper.selectByName(username);
    }

    /** * The method provided by the UserDetailsService interface to obtain user information based on the user name **@param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // Call our own findUserByName method
        return this.findUserByName(username); }}Copy the code

2.4 SecurityConfig Configuration class

/ * * *@Auther: csp1999
 * @Date: 2020/12/02/18:41
 * @Description: Spring Security configuration class */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    / * * * for certification process (core) * < p > * the AuthenticationManager: core interface for certification. * AuthenticationManagerBuilder: Tool for building the AuthenticationManager interface object. * ProviderManager: default implementation class of the AuthenticationManager interface. * *@param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// Built-in (default) authentication rules:
// auth.userDetailsService(userService)
// Code the password,Pbkdf2PasswordEncoder Salt value attached to the encryption
// .passwordEncoder(new Pbkdf2PasswordEncoder("securtyu"));
        /** * Custom authentication rule: * AuthenticationProvider: ProviderManager holds a set of AuthenticationProviders, * each of which is responsible for one authentication. * * This design pattern is called the delegate pattern: ProviderManager delegates authentication to AuthenticationProvider. */
        auth.authenticationProvider(new AuthenticationProvider() {
            /** * Authentication: interface for encapsulating Authentication information. Different implementation classes represent different types of Authentication information@param authentication
             * @return
             * @throws AuthenticationException
             */
            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                // Get the user name
                String username = authentication.getName();
                // Get the password
                String password = (String) authentication.getCredentials();

                User user = userService.findUserByName(username);
                if (user == null) {
                    throw new UsernameNotFoundException("Account does not exist!");
                }

                // Encrypt the password and query it in the database
                password = CommunityUtil.md5(password + user.getSalt());
                if(! user.getPassword().equals(password)) {throw new BadCredentialsException("Incorrect password!");
                }

                // Principal: the principal information of the authentication (such as the user object);
                // Credentials: certificates (in account and password mode, certificates use the password password).
                // authorities:
                return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
            }

            /** * What type of authentication is supported by the current AuthenticationProvider. **@param authentication
             * @return* /
            @Override
            public boolean supports(Class
        authentication) {
                // Support account and password authentication mode:
                / / UsernamePasswordAuthenticationToken: Authentication interface implementation class in common use.
                returnUsernamePasswordAuthenticationToken.class.equals(authentication); }}); }/** * used for authorization processing (core) **@param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
// The parent authorization logic. For more information, click super to view the source code
// super.configure(http);

        // Login page configuration
        http.formLogin()
                // Specify the login page
                .loginPage("/loginpage")
                // The path to process the login request
                .loginProcessingUrl("/login")
// Indicates the path to jump to when the login succeeds
// .successForwardUrl("/xxx")
// Path to jump to when login fails
// .failureForwardUrl("/xxx")
                // After successful login, the corresponding handler is processed
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                            throws IOException, ServletException {
                        // Redirect to the home page
                        response.sendRedirect(request.getContextPath() + "/index"); }})// Handler that processes the login failure
                .failureHandler(new AuthenticationFailureHandler() {
                    @Override
                    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
                            throws IOException, ServletException {
                        // Store error information
                        request.setAttribute("error", exception.getMessage());
                        // Forward to the login page
                        request.getRequestDispatcher("/loginpage").forward(request, response); }});// Exit related configurations
        http.logout()
                // Process the logout request
                .logoutUrl("/logout")
                // After logging out, handle the corresponding handler
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
                            throws IOException, ServletException {
                        // Redirect to the home page
                        response.sendRedirect(request.getContextPath() + "/index"); }});// Authorization configuration
        http.authorizeRequests()
                .antMatchers("/letter").hasAnyAuthority("USER"."ADMIN")
                .antMatchers("/admin").hasAnyAuthority("ADMIN")
                // If you do not have permission, go to the message denied page
                .and().exceptionHandling().accessDeniedPage("/denied");

        // Add a Filter to process the captcha
        http.addFilterBefore(new Filter() {
            @Override
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                HttpServletRequest request = (HttpServletRequest) servletRequest;
                HttpServletResponse response = (HttpServletResponse) servletResponse;

                if (request.getServletPath().equals("/login")) {
                    String verifyCode = request.getParameter("verifyCode");
                    // The verification code is written to a dead end.
                    // We need to add a verification code generation method and store the verification code in cookie or redis
                    // This is just for demonstration purposes:
                    if (verifyCode == null| |! verifyCode.equalsIgnoreCase("1234")) {
                        request.setAttribute("error"."Verification code error!");
                        request.getRequestDispatcher("/loginpage").forward(request, response);
                        return; }}// Let the request continue down.
                filterChain.doFilter(request, response);
            }
            / / add the Filter new Filter () will be performed before UsernamePasswordAuthenticationFilter filters
        }, UsernamePasswordAuthenticationFilter.class);

        / / remember me
        http.rememberMe()
                / / store user data: new InMemoryTokenRepositoryImpl () the user data is stored in memory
                // If you want to store cookies or tokens, you need to write your own implementation method and new the method instance here
                .tokenRepository(new InMemoryTokenRepositoryImpl())
                // The expiration time is 24 hours
                .tokenValiditySeconds(3600 * 24)
                / / specified userService
                .userDetailsService(userService);
    }

    /** * used to configure some intercepting resources **@param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        // Ignore all static resources under resources
        web.ignoring().antMatchers("/resources/**"); }}Copy the code

2.5 Controller Layer Modification

/ * * *@Auther: csp1999
 * @Date: 2020/12/02/17:28
 * @Description: * /
@Controller
public class HomeController {

    @RequestMapping(path = "/index", method = RequestMethod.GET)
    public String getIndexPage(Model model) {
        // After successful authentication, the resulting user information is stored into the SecurityContext via SecurityContextHolder.
        Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (obj instanceof User) {
            model.addAttribute("loginUser", obj);
        }
        return "/index";
    }

    @RequestMapping(path = "/discuss", method = RequestMethod.GET)
    public String getDiscussPage(a) {
        return "/site/discuss";
    }

    @RequestMapping(path = "/letter", method = RequestMethod.GET)
    public String getLetterPage(a) {
        return "/site/letter";
    }

    @RequestMapping(path = "/admin", method = RequestMethod.GET)
    public String getAdminPage(a) {
        return "/site/admin";
    }

    @RequestMapping(path = "/loginpage", method = {RequestMethod.GET, RequestMethod.POST})
    public String getLoginPage(a) {
        return "/site/login";
    }

    // Reject access to the prompt page
    @RequestMapping(path = "/denied", method = RequestMethod.GET)
    public String getDeniedPage(a) {
        return "/error/404"; }}Copy the code

2.6 HTML Page Modification

index.html

<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>
    <h1>Community home page</h1>
    <! Welcome message -->
    <p th:if="${loginUser! =null}">Welcome you,<span th:text="${loginUser.username}"></span>!
    </p>
    <ul>
        <li><a th:href="@{/discuss}">Post details</a></li>
        <li><a th:href="@{/letter}">Direct messages list</a></li>
        <li><a th:href="@{/loginpage}">The login</a></li>
        <! <li><a th:href="@{/loginpage}"> exit </a></li>-->
        <li>
            <form method="post" th:action="@{/logout}">
                <a href="javascript:document.forms[0].submit();">exit</a>
            </form>
        </li>
    </ul>
</body>
</html>
Copy the code

login.html

<! DOCTYPEhtml>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>The login</title>
</head>
<body>
<h1>Log in the community</h1>
<form method="post" th:action="@{/login}">
    <p style="color:red;" th:text="${error}">
        <! -- Prompt message -->
    </p>
    <p>Account:<input type="text" name="username" th:value="${param.username}">
    </p>
    <p>Password:<input type="password" name="password" th:value="${param.password}">
    </p>
    <p>Verification code:<input type="text" name="verifyCode"> <i>1234</i>
    </p>
    <p>
        <input type="checkbox" name="remember-me">Remember that I</p>
    <p>
        <input type="submit" value="Login">
    </p>
</form>
</body>
</html>
Copy the code

3. Test the effect

3.1 Accessing the Home Page

http://localhost:8080/community/index

Try the post details and private message list page without logging in:

As you can see, no permission is blocked to the login page, and then we login:

Next, enter the post details and private message list page in the next test:

Have authority, can enter!

If the article is helpful to you, click “like” to support it, then we will update an article about the niuke forum project actual combat, practice the use of SpringSecurity ~