Преглед изворни кода

1. 新增岗位实体类、服务类
2. 新增岗位标签实体类、服务类
3. 完善前端页面分页组件
4. 完善用户登录逻辑

王育民 пре 5 година
родитељ
комит
97a5e5f2f3

+ 65 - 2
src/main/java/cn/minbb/job/config/WebSecurityConfig.java

@@ -1,27 +1,90 @@
 package cn.minbb.job.config;
 
+import cn.minbb.job.handler.LoginAuthenticationFailureHandler;
+import cn.minbb.job.handler.LoginAuthenticationSuccessHandler;
+import cn.minbb.job.handler.LogoutSuccessHandler;
+import cn.minbb.job.model.repository.UserRepository;
+import cn.minbb.job.service.impl.UserServiceImpl;
+import lombok.extern.log4j.Log4j2;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.BadCredentialsException;
+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.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 
 @Configuration
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true)
+@Log4j2
 @Order(2)
 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
+    private final LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;
+    private final LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;
+    private final LogoutSuccessHandler logoutSuccessHandler;
+    private final UserRepository userRepository;
+
+    public WebSecurityConfig(LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler, LoginAuthenticationFailureHandler loginAuthenticationFailureHandler, LogoutSuccessHandler logoutSuccessHandler, UserRepository userRepository) {
+        this.loginAuthenticationSuccessHandler = loginAuthenticationSuccessHandler;
+        this.loginAuthenticationFailureHandler = loginAuthenticationFailureHandler;
+        this.logoutSuccessHandler = logoutSuccessHandler;
+        this.userRepository = userRepository;
+    }
+
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.antMatcher("/**").authorizeRequests()
                 .antMatchers("/login**").permitAll()
-                .antMatchers("/admin/**").hasAnyRole("ADMIN_SYSTEM", "ADMIN_BLOG")
+                .antMatchers("/admin/**").hasAnyRole("ADMIN")
+                // 其他地址的访问均放行
                 .anyRequest().permitAll()
+                .and().formLogin().loginPage("/login").loginProcessingUrl("/login")
+                // 用户名字段和密码字段
+                .usernameParameter("username").passwordParameter("password")
+                // 验证失败后跳转的请求
+                .failureUrl("/login?error=true")
+                .successHandler(loginAuthenticationSuccessHandler)
+                .failureHandler(loginAuthenticationFailureHandler)
                 .and().logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
-                .logoutSuccessUrl("/logout")
+                .logoutSuccessUrl("/login?logout").logoutSuccessHandler(logoutSuccessHandler)
                 .and().csrf().disable();
     }
+
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        web.ignoring().antMatchers(HttpMethod.GET, "/images/**", "/plugins/**");
+        web.ignoring().antMatchers(HttpMethod.GET, "/**/*.css");
+        web.ignoring().antMatchers(HttpMethod.GET, "/**/*.js");
+        web.ignoring().mvcMatchers(HttpMethod.GET, "/favicon.ico");
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(new UserServiceImpl(userRepository)).passwordEncoder(new BCryptPasswordEncoder() {
+            @Override
+            public String encode(CharSequence rawPassword) {
+                log.info("密码加密 1 = {}", rawPassword);
+                log.info("密码加密 2 = {}", new BCryptPasswordEncoder().encode(rawPassword));
+                return new BCryptPasswordEncoder().encode(rawPassword);
+            }
+
+            @Override
+            public boolean matches(CharSequence rawPassword, String encodedPassword) {
+                log.info("密码验证逻辑 3 = {}", rawPassword);
+                log.info("密码验证逻辑 4 = {}", encodedPassword);
+                log.info("密码验证逻辑 5 = {}", new BCryptPasswordEncoder().encode(rawPassword));
+                if (!encodedPassword.contentEquals(rawPassword)) {
+                    throw new BadCredentialsException("密码错误!");
+                }
+                return true;
+            }
+        });
+    }
 }

+ 1 - 1
src/main/java/cn/minbb/job/controller/web/CompanyController.java

@@ -33,7 +33,7 @@ public class CompanyController {
             @RequestParam(name = "id", required = false) Integer id,
             @RequestParam(name = "industry", required = false) Integer industryId,
             @RequestParam(name = "page", defaultValue = "1", required = false) Integer page,
-            @RequestParam(name = "size", defaultValue = "12", required = false) Integer size,
+            @RequestParam(name = "size", defaultValue = "9", required = false) Integer size,
             ModelAndView modelAndView) {
         Company company = companyService.findOneById(id);
         if (null == company) {

+ 2 - 0
src/main/java/cn/minbb/job/enumerate/Role.java

@@ -5,6 +5,8 @@ import lombok.Getter;
 @Getter
 public enum Role {
     USER("用户"),
+    COMPANY("企业"),
+    SCHOOL("学校"),
     ADMIN("管理员");
 
     String description;

+ 13 - 2
src/main/java/cn/minbb/job/model/Company.java

@@ -6,6 +6,7 @@ import lombok.NoArgsConstructor;
 
 import javax.persistence.*;
 import java.util.List;
+import java.util.Set;
 
 /**
  * 企业
@@ -21,15 +22,21 @@ import java.util.List;
 )
 public class Company extends Auditable {
 
-    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '标题'")
+    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '名称'")
     private String name;
 
     @Column(name = "brand", nullable = false, columnDefinition = "VARCHAR(255) COMMENT '商标'")
     private String brand;
 
-    @Column(name = "introduction", columnDefinition = "VARCHAR(500) COMMENT '简介'")
+    @Column(name = "introduction", nullable = false, columnDefinition = "VARCHAR(500) COMMENT '简介'")
     private String introduction;
 
+    @Column(name = "nature", nullable = false, columnDefinition = "VARCHAR(16) COMMENT '性质'")
+    private String nature;
+
+    @Column(name = "scale", nullable = false, columnDefinition = "VARCHAR(16) COMMENT '规模'")
+    private String scale;
+
     @Column(name = "description", columnDefinition = "VARCHAR(1000) COMMENT '描述'")
     private String description;
 
@@ -39,6 +46,10 @@ public class Company extends Auditable {
     @Column(name = "is_enabled", nullable = false, columnDefinition = "TINYINT DEFAULT '1' COMMENT '启用'")
     private Boolean isEnabled;
 
+    // 岗位(集合)
+    @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, mappedBy = "company", fetch = FetchType.LAZY)
+    private Set<Job> jobSet;
+
     @ManyToMany(cascade = {CascadeType.MERGE})
     @JoinTable(name = "company_industry",
             joinColumns = {@JoinColumn(name = "company_id", referencedColumnName = "id", columnDefinition = "INT COMMENT '企业ID'")},

+ 1 - 1
src/main/java/cn/minbb/job/model/Industry.java

@@ -23,7 +23,7 @@ import javax.persistence.Table;
 )
 public class Industry extends Auditable {
 
-    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '标题'")
+    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '名称'")
     private String name;
 
     @Column(name = "description", columnDefinition = "VARCHAR(1000) COMMENT '描述'")

+ 59 - 0
src/main/java/cn/minbb/job/model/Job.java

@@ -0,0 +1,59 @@
+package cn.minbb.job.model;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+import java.util.List;
+
+/**
+ * 岗位
+ */
+@Data
+@Entity
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@Table(name = "job",
+        indexes = {
+                @Index(name = "index_name", columnList = "name")
+        }
+)
+public class Job extends Auditable {
+
+    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '名称'")
+    private String name;
+
+    @Column(name = "salary", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '薪资待遇'")
+    private String salary;
+
+    @Column(name = "place", columnDefinition = "VARCHAR(32) COMMENT '工作地点'")
+    private String place;
+
+    @Column(name = "education", columnDefinition = "VARCHAR(32) COMMENT '学历'")
+    private String education;
+
+    @Column(name = "description", columnDefinition = "VARCHAR(1000) COMMENT '描述'")
+    private String description;
+
+    @Column(name = "priority", columnDefinition = "INT COMMENT '优先级'")
+    private Integer priority;
+
+    @Column(name = "is_enabled", nullable = false, columnDefinition = "TINYINT DEFAULT '1' COMMENT '启用'")
+    private Boolean isEnabled;
+
+    @ManyToOne(cascade = {CascadeType.MERGE}, optional = false)
+    @JoinColumn(name = "company_id", columnDefinition = "INT COMMENT '企业ID'")
+    private Company company;
+
+    @ManyToMany(cascade = {CascadeType.MERGE})
+    @JoinTable(name = "job_job_tag",
+            joinColumns = {@JoinColumn(name = "job_id", referencedColumnName = "id", columnDefinition = "INT COMMENT '岗位ID'")},
+            inverseJoinColumns = {@JoinColumn(name = "job_tag_id", referencedColumnName = "id", columnDefinition = "INT COMMENT '岗位标签ID'")},
+            indexes = {
+                    @Index(name = "index_job_id", columnList = "job_id"),
+                    @Index(name = "index_job_tag_id", columnList = "job_tag_id")
+            }
+    )
+    private List<JobTag> jobTagList;
+}

+ 34 - 0
src/main/java/cn/minbb/job/model/JobTag.java

@@ -0,0 +1,34 @@
+package cn.minbb.job.model;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Index;
+import javax.persistence.Table;
+
+/**
+ * 岗位标签
+ */
+@Data
+@Entity
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@Table(name = "job_tag",
+        indexes = {
+                @Index(name = "index_name", columnList = "name")
+        }
+)
+public class JobTag extends Auditable {
+
+    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(16) COMMENT '名称'")
+    private String name;
+
+    @Column(name = "priority", columnDefinition = "INT COMMENT '优先级'")
+    private Integer priority;
+
+    @Column(name = "is_enabled", nullable = false, columnDefinition = "TINYINT DEFAULT '1' COMMENT '启用'")
+    private Boolean isEnabled;
+}

+ 1 - 1
src/main/java/cn/minbb/job/model/Resume.java

@@ -23,7 +23,7 @@ import javax.persistence.Table;
 )
 public class Resume extends Auditable {
 
-    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '标题'")
+    @Column(name = "name", nullable = false, columnDefinition = "VARCHAR(32) COMMENT '姓名'")
     private String name;
 
     @Column(name = "photo", nullable = false, columnDefinition = "VARCHAR(255) COMMENT '照片'")

+ 7 - 0
src/main/java/cn/minbb/job/model/repository/JobRepository.java

@@ -0,0 +1,7 @@
+package cn.minbb.job.model.repository;
+
+import cn.minbb.job.model.Job;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface JobRepository extends JpaRepository<Job, Integer> {
+}

+ 7 - 0
src/main/java/cn/minbb/job/model/repository/JobTagRepository.java

@@ -0,0 +1,7 @@
+package cn.minbb.job.model.repository;
+
+import cn.minbb.job.model.JobTag;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface JobTagRepository extends JpaRepository<JobTag, Integer> {
+}

+ 1 - 0
src/main/java/cn/minbb/job/model/repository/UserRepository.java

@@ -6,4 +6,5 @@ import org.springframework.stereotype.Repository;
 
 @Repository
 public interface UserRepository extends JpaRepository<User, Integer> {
+    User findOneByUsername(String username);
 }

+ 4 - 0
src/main/java/cn/minbb/job/service/JobService.java

@@ -0,0 +1,4 @@
+package cn.minbb.job.service;
+
+public interface JobService {
+}

+ 4 - 0
src/main/java/cn/minbb/job/service/JobTagService.java

@@ -0,0 +1,4 @@
+package cn.minbb.job.service;
+
+public interface JobTagService {
+}

+ 4 - 1
src/main/java/cn/minbb/job/service/UserService.java

@@ -1,9 +1,12 @@
 package cn.minbb.job.service;
 
 import cn.minbb.job.model.User;
+import org.springframework.security.core.userdetails.UserDetailsService;
 
-public interface UserService {
+public interface UserService extends UserDetailsService {
     User saveOne(User user);
 
     User findOneById(Integer id);
+
+    User findUserByUsername(String username);
 }

+ 15 - 0
src/main/java/cn/minbb/job/service/impl/JobServiceImpl.java

@@ -0,0 +1,15 @@
+package cn.minbb.job.service.impl;
+
+import cn.minbb.job.model.repository.JobRepository;
+import cn.minbb.job.service.JobService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class JobServiceImpl implements JobService {
+
+    private final JobRepository jobRepository;
+
+    public JobServiceImpl(JobRepository jobRepository) {
+        this.jobRepository = jobRepository;
+    }
+}

+ 15 - 0
src/main/java/cn/minbb/job/service/impl/JobTagServiceImpl.java

@@ -0,0 +1,15 @@
+package cn.minbb.job.service.impl;
+
+import cn.minbb.job.model.repository.JobTagRepository;
+import cn.minbb.job.service.JobTagService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class JobTagServiceImpl implements JobTagService {
+
+    private final JobTagRepository jobTagRepository;
+
+    public JobTagServiceImpl(JobTagRepository jobTagRepository) {
+        this.jobTagRepository = jobTagRepository;
+    }
+}

+ 44 - 0
src/main/java/cn/minbb/job/service/impl/UserServiceImpl.java

@@ -1,10 +1,23 @@
 package cn.minbb.job.service.impl;
 
 import cn.minbb.job.model.User;
+import cn.minbb.job.model.UserRole;
 import cn.minbb.job.model.repository.UserRepository;
 import cn.minbb.job.service.UserService;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.CredentialsExpiredException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
 @Service
 public class UserServiceImpl implements UserService {
 
@@ -23,4 +36,35 @@ public class UserServiceImpl implements UserService {
     public User findOneById(Integer id) {
         return userRepository.findById(id).orElse(null);
     }
+
+    @Override
+    public User findUserByUsername(String username) {
+        return userRepository.findOneByUsername(username);
+    }
+
+    @Override
+    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
+        User user = userRepository.findOneByUsername(s);
+        if (user == null) {
+            throw new BadCredentialsException("用户名不存在");
+        } else {
+            if (!user.isAccountNonExpired()) {
+                throw new BadCredentialsException("账户已过期");
+            } else if (!user.isAccountNonLocked()) {
+                throw new LockedException("账户被锁定");
+            } else if (!user.isCredentialsNonExpired()) {
+                throw new CredentialsExpiredException("凭据已过期");
+            } else if (!user.isEnabled()) {
+                throw new DisabledException("账户被禁用");
+            } else {
+                List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
+                Set<UserRole> userRoleSet = user.getUserRoleSet();
+                for (UserRole userRole : userRoleSet) {
+                    grantedAuthorityList.add(new SimpleGrantedAuthority("ROLE_" + userRole.getRole().name()));
+                }
+                new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), grantedAuthorityList);
+            }
+        }
+        return user;
+    }
 }

+ 5 - 9
src/main/resources/templates/companies.html

@@ -79,18 +79,14 @@
 
             <nav aria-label="Page navigation" style="margin-top: 24px;" th:if="${TOTAL_PAGES != 0}">
                 <ul class="pagination justify-content-center">
-                    <li class="page-item disabled">
-                        <a class="page-link" href="#!" tabindex="-1" th:href="${'?page=' + (PAGE - 1)}">&laquo;</a>
+                    <li class="page-item" th:classappend="${PAGE <= 1}? 'disabled'">
+                        <a class="page-link" tabindex="-1" th:href="${'?page=' + (PAGE - 1)}">&laquo;</a>
                     </li>
                     <li class="page-item" th:each="i : ${#numbers.sequence(1, TOTAL_PAGES)}" th:classappend="${PAGE == i}? 'active'">
-                        <a class="page-link" th:href="${'?page=' + i}" th:text="${i}">1</a>
+                        <a class="page-link" th:href="${'?page=' + i}">[[${i}]] <span class="sr-only">(current)</span></a>
                     </li>
-                    <li class="page-item active">
-                        <a class="page-link" href="#!">2 <span class="sr-only"></span></a>
-                    </li>
-                    <li class="page-item"><a class="page-link" href="#!">3</a></li>
-                    <li class="page-item">
-                        <a class="page-link" href="#!" th:href="${'?page=' + (PAGE + 1)}">&raquo;</a>
+                    <li class="page-item" th:classappend="${PAGE >= TOTAL_PAGES}? 'disabled'">
+                        <a class="page-link" th:href="${'?page=' + (PAGE + 1)}">&raquo;</a>
                     </li>
                 </ul>
             </nav>

+ 1 - 1
src/main/resources/templates/register.html

@@ -79,7 +79,7 @@
                         <li>就业咨询</li>
                         <li>保障就业</li>
                     </ul>
-                    <a class="btn btn-lg btn-block btn-primary" type="button" href="#modal-school" data-toggle="modal">立即注册</a>
+                    <button class="btn btn-lg btn-block btn-primary" type="button" href="#modal-school" data-toggle="modal">立即注册</button>
                 </div>
             </div>
             <div class="card mb-4 box-shadow">