SpringBoot+vue 前後端的分離專案筆記 [二] 使用者登入驗證

semlie1994發表於2020-05-15

使用者登入驗證主要涉及Token+Jpa+BCryptPasswordEncoder密碼驗證。

SpringBoot方面

@Entity(name ="manager")
@Data
public class User extends AbstractEntity {


    String username;

    String password;

    String email;

}
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AbstractEntity {

    @Id
    @GeneratedValue
    @Getter
    @Setter
    Long id;

    @CreatedDate
    LocalDateTime createdDate;
    @LastModifiedDate
    LocalDateTime modifiedDate;
}

建立對應實體類的Jpa操作介面

public interface UserRespority extends Repository<User,Long> {

    Optional<User> findById(Long id);
    Optional<User> findByUsername(String username);
}

tokan方面參考連線:vue+springboot前後端分離實現token登入驗證和狀態儲存的簡單實現方案
修改Token工具包,方便後面提取token中的使用者資訊

TokenUtil.java

  /**
     * 簽名驗證
     * @param token
     * @return
     */
    public static User verify(String token){
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT jwt = verifier.verify(token);
            User user = new User();
            user.setId(jwt.getClaim("useid").asLong());
            user.setUsername(jwt.getClaim("username").asString());
            return user;
        } catch (Exception e){
            return null;
        }
    }

修改tokan攔截器,提取token中的使用者資訊到request

TokenInterceptor.java

......
 response.setCharacterEncoding("utf-8");
 String token = request.getHeader("Authorization");

  if(token != null){
      User result = TokenUtil.verify(token);
          if(result != null){
               request.setAttribute("user",result);
               return true;
         }
    }
......

整合過濾請求,跨域,以及後面用到的密碼驗證的安全配置
具體配置教程參考

/**
 * 安全配置類
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
    private TokenInterceptor tokenInterceptor;
    //構造方法
    public WebSecurityConfig(TokenInterceptor token){
        this.tokenInterceptor = token;
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedHeaders("*")
                .allowedMethods("*")
                .allowedOrigins("*");
    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer){
        configurer.setTaskExecutor(new ConcurrentTaskExecutor(Executors.newFixedThreadPool(3)));
        configurer.setDefaultTimeout(30000);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        List<String> excludePath = new ArrayList<>();
        //排除攔截,除了註冊登入(此時還沒token),其他都攔截
        excludePath.add("/user/register");  //註冊
        excludePath.add("/user/getImgCode");  //獲取驗證碼
        excludePath.add("/user/login");     //登入
        excludePath.add("/static/**");  //靜態資源
        excludePath.add("/assets/**");  //靜態資源

        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);
    }

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 1允許任何域名使用
        corsConfiguration.addAllowedOrigin("*");
        // 2允許任何頭
        corsConfiguration.addAllowedHeader("*");
        // 3允許任何方法(post、get等)
        corsConfiguration.addAllowedMethod("*");
        return corsConfiguration;
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/**").permitAll()
                .anyRequest().authenticated()
                .and().csrf().disable();
    }
}

BCryptPasswordEncoder密碼驗證

使用BCryptPasswordEncoder主要是因為資料庫的使用者登入密碼是另一個laravel專案中加密的,要在SpringBoot中驗證登入的話,就需要用到BCryptPasswordEncoder。(如不需要,可略過)
具體配置,參考教程:Spring security中的BCryptPasswordEncoder方法對密碼進行加密與密碼匹配

修改控制器

增加登入驗證以及token驗證

@RestController
@SpringBootApplication
@RequestMapping("/user")
public class LoginControll {

    @Autowired
   private UserRespority userRespority;

    //生成驗證碼
    @RequestMapping(value = "/getImgCode")
    @ResponseBody
    public Map<String, String> getImgCode() {
        Map<String, String> result = new HashMap<>();
        try {
            result = ImgValidateCodeUtil.getImgCodeBaseCode(4);
        } catch (Exception e) {
            System.out.println(e);
        }
        return result;
    }
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    @ResponseBody
    public String login(@RequestBody Map<String,Object> para) throws JsonProcessingException {
        HashMap<String,Object> hs=new HashMap<>();
        try {
            //獲取使用者資訊
            User user = userRespority.findByUsername((String)para.get("username")).get();
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//            System.out.println(user.getId());
            if(bCryptPasswordEncoder.matches((String)para.get("password"),user.getPassword())){
                String token= TokenUtil.sign(user);
                hs.put("code",200);
                hs.put("token",token);
            }else {
                hs.put("code",400);
                hs.put("msg","密碼錯誤");
            }

        }catch (NoSuchElementException e){
            hs.put("code",400);
            hs.put("msg","使用者名稱不存在");
            ObjectMapper objectMapper=new ObjectMapper();
        }
        ObjectMapper objectMapper=new ObjectMapper();
        return objectMapper.writeValueAsString(hs);
    }

    @RequestMapping(value = "/yztokan")
    @ResponseBody
    public Map<String, String> yztokan(@RequestAttribute(value = "user") User user){
        Map<String, String> result = new HashMap<>();
        result.put("user",user.getUsername());
        return  result;
    }

}

vue方面

token配置參考教程: vue+springboot前後端分離實現token登入驗證和狀態儲存的簡單實現方案

修改登入方法

 /*登入*/
            tologin:function () {
                // this.$axios.post('user/getImgCode')
                let v = this.loginForm;
             if (v.inputCode === this.code){

                 // this.$layer.alert("驗證通過!");
                 this.$axios.post('user/login',{
                     'username':v.username,
                     'password':v.password
                 }).then((response) => {
                     // console.log(response)
                     if (response.data.code === 200){
                         this.userToken = response.data.token;
                         this.changeLogin({Authorization:this.userToken});
                         this.$layer.msg("登入成功!");
                         // console.log(this.userToken)
                         this.$router.push('/');
                     }else {
                         this.getIdentifyingCode();
                         this.$layer.alert(response.data.msg);
                     }

                 }).catch((error) => {
                     console.log(error)
                 });
             }else {
                 this.$layer.alert("驗證錯誤");
             }
            }
        }

增加token驗證方法

ceshi:function () {
       // console.log(localStorage.getItem('Authorization'))
       this.$axios.get('user/yztokan')
               .then((response) => {

                console.log(response)
               })
               .catch((error) => {
                console.log(error);
               });
      }

效果圖

SpringBoot+vue 前後端的分離專案筆記 [二] 使用 Token+Jpa+BCryptPasswordEncoder密碼驗證

SpringBoot+vue 前後端的分離專案筆記 [二] 使用 Token+Jpa+BCryptPasswordEncoder密碼驗證

SpringBoot+vue 前後端的分離專案筆記 [二] 使用 Token+Jpa+BCryptPasswordEncoder密碼驗證

其他參考教程:
官方:Spring Data JPA
相關程式碼:github.com/spring-projects/spring-...
vue怎麼修改title
vue專案中使用vue-layer彈框外掛

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章