亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

SpringBoot+SpringSecurity處理Ajax登錄請(qǐng)求問題(推薦)

 更新時(shí)間:2017年12月20日 11:08:00   作者:獨(dú)孤求敗  
這篇文章主要介紹了SpringBoot+SpringSecurity處理Ajax登錄請(qǐng)求問題,本文給大家介紹的非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

最近在項(xiàng)目中遇到了這樣一個(gè)問題:前后端分離,前端用Vue來做,所有的數(shù)據(jù)請(qǐng)求都使用vue-resource,沒有使用表單,因此數(shù)據(jù)交互都是使用JSON,后臺(tái)使用Spring Boot,權(quán)限驗(yàn)證使用了Spring Security,因?yàn)橹坝肧pring Security都是處理頁面的,這次單純處理Ajax請(qǐng)求,因此記錄下遇到的一些問題。這里的解決方案不僅適用于Ajax請(qǐng)求,也可以解決移動(dòng)端請(qǐng)求驗(yàn)證。

創(chuàng)建工程

首先我們需要?jiǎng)?chuàng)建一個(gè)Spring Boot工程,創(chuàng)建時(shí)需要引入Web、Spring Security、MySQL和MyBatis(數(shù)據(jù)庫框架其實(shí)隨意,我這里使用MyBatis),創(chuàng)建好之后,依賴文件如下:

<dependency>
 <groupId>org.mybatis.spring.boot</groupId>
 <artifactId>mybatis-spring-boot-starter</artifactId>
 <version>1.3.1</version>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <scope>runtime</scope>
</dependency>
<dependency>
 <groupId>commons-codec</groupId>
 <artifactId>commons-codec</artifactId>
 <version>1.11</version>
</dependency>

注意最后一個(gè) commons-codec 依賴是我手動(dòng)加入進(jìn)來的,這是一個(gè)Apache的開源項(xiàng)目,可以用來生成MD5消息摘要,我在后文中將對(duì)密碼進(jìn)行簡(jiǎn)單的處理。

創(chuàng)建數(shù)據(jù)庫并配置

為了簡(jiǎn)化邏輯,我這里創(chuàng)建了三個(gè)表,分別是用戶表、角色表、用戶角色關(guān)聯(lián)表,如下:

接下來我們需要在application.properties中對(duì)自己的數(shù)據(jù)庫進(jìn)行簡(jiǎn)單的配置,這里各位小伙伴視自己的具體情況而定。

spring.datasource.url=jdbc:mysql:///vueblog
spring.datasource.username=root
spring.datasource.password=123

構(gòu)造實(shí)體類

這里主要是指構(gòu)造用戶類,這里的用戶類比較特殊,必須實(shí)現(xiàn)UserDetails接口,如下:

public class User implements UserDetails {
 private Long id;
 private String username;
 private String password;
 private String nickname;
 private boolean enabled;
 private List<Role> roles;
 @Override
 public boolean isAccountNonExpired() {
 return true;
 }
 @Override
 public boolean isAccountNonLocked() {
 return true;
 }
 @Override
 public boolean isCredentialsNonExpired() {
 return true;
 }
 @Override
 public boolean isEnabled() {
 return enabled;
 }
 @Override
 public List<GrantedAuthority> getAuthorities() {
 List<GrantedAuthority> authorities = new ArrayList<>();
 for (Role role : roles) {
  authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getName()));
 }
 return authorities;
 }
 //getter/setter省略...
}

實(shí)現(xiàn)了UserDetails接口之后,該接口中有幾個(gè)方法需要我們實(shí)現(xiàn),四個(gè)返回Boolean的方法都是見名知意,enabled表示檔期賬戶是否啟用,這個(gè)我數(shù)據(jù)庫中確實(shí)有該字段,因此根據(jù)查詢結(jié)果返回,其他的為了簡(jiǎn)單期間都直接返回true,getAuthorities方法返回當(dāng)前用戶的角色信息,用戶的角色其實(shí)就是roles中的數(shù)據(jù),將roles中的數(shù)據(jù)轉(zhuǎn)換為L(zhǎng)ist<GrantedAuthority>之后返回即可, 這里有一個(gè)要注意的地方,由于我在數(shù)據(jù)庫中存儲(chǔ)的角色名都是諸如‘超級(jí)管理員'、‘普通用戶'之類的,并不是以 ROLE_ 這樣的字符開始的,因此需要在這里手動(dòng)加上 ROLE_ ,切記 。

另外還有一個(gè)Role實(shí)體類,比較簡(jiǎn)單,按照數(shù)據(jù)庫的字段創(chuàng)建即可,這里不再贅述。

創(chuàng)建UserService

這里的UserService也比較特殊,需要實(shí)現(xiàn)UserDetailsService接口,如下:

@Service
public class UserService implements UserDetailsService {
 @Autowired
 UserMapper userMapper;
 @Autowired
 RolesMapper rolesMapper;
 @Override
 public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  User user = userMapper.loadUserByUsername(s);
  if (user == null) {
   //避免返回null,這里返回一個(gè)不含有任何值的User對(duì)象,在后期的密碼比對(duì)過程中一樣會(huì)驗(yàn)證失敗
   return new User();
  }
  //查詢用戶的角色信息,并返回存入user中
  List<Role> roles = rolesMapper.getRolesByUid(user.getId());
  user.setRoles(roles);
  return user;
 }
}

實(shí)現(xiàn)了UserDetailsService接口之后,我們需要實(shí)現(xiàn)該接口中的loadUserByUsername方法,即根據(jù)用戶名查詢用戶。這里注入了兩個(gè)MyBatis中的Mapper,UserMapper用來查詢用戶,RolesMapper用來查詢角色。在loadUserByUsername方法中,首先根據(jù)傳入的參數(shù)(參數(shù)就是用戶登錄時(shí)輸入的用戶名)去查詢用戶,如果查到的用戶為null,可以直接拋一個(gè)UsernameNotFoundException異常,但是我為了處理方便,返回了一個(gè)沒有任何值的User對(duì)象,這樣在后面的密碼比對(duì)過程中一樣會(huì)發(fā)現(xiàn)登錄失敗的(這里大家根據(jù)自己的業(yè)務(wù)需求調(diào)整即可),如果查到的用戶不為null,此時(shí)我們根據(jù)查到的用戶id再去查詢?cè)撚脩舻慕巧?,并將查詢結(jié)果放入到user對(duì)象中,這個(gè)查詢結(jié)果將在user對(duì)象的getAuthorities方法中用上。

Security配置

我們先來看一下我的Security配置,然后我再來一一解釋:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 UserService userService;
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.userDetailsService(userService).passwordEncoder(new PasswordEncoder() {
   @Override
   public String encode(CharSequence charSequence) {
    return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
   }
   /**
    * @param charSequence 明文
    * @param s 密文
    * @return
    */
   @Override
   public boolean matches(CharSequence charSequence, String s) {
    return s.equals(DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()));
   }
  });
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  http.authorizeRequests()
    .antMatchers("/admin/**").hasRole("超級(jí)管理員")
    .anyRequest().authenticated()//其他的路徑都是登錄后即可訪問
    .and().formLogin().loginPage("/login_page").successHandler(new AuthenticationSuccessHandler() {
   @Override
   public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
    httpServletResponse.setContentType("application/json;charset=utf-8");
    PrintWriter out = httpServletResponse.getWriter();
    out.write("{\"status\":\"ok\",\"msg\":\"登錄成功\"}");
    out.flush();
    out.close();
   }
  })
    .failureHandler(new AuthenticationFailureHandler() {
     @Override
     public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
      httpServletResponse.setContentType("application/json;charset=utf-8");
      PrintWriter out = httpServletResponse.getWriter();
      out.write("{\"status\":\"error\",\"msg\":\"登錄失敗\"}");
      out.flush();
      out.close();
     }
    }).loginProcessingUrl("/login")
    .usernameParameter("username").passwordParameter("password").permitAll()
    .and().logout().permitAll().and().csrf().disable();
 }
 @Override
 public void configure(WebSecurity web) throws Exception {
  web.ignoring().antMatchers("/reg");
 }
}

這是我們配置的核心,小伙伴們聽我一一道來:

1.首先這是一個(gè)配置類,因此記得加上@Configuration注解,又因?yàn)檫@是Spring Security的配置,因此記得繼承WebSecurityConfigurerAdapter。

2.將剛剛創(chuàng)建好的UserService注入進(jìn)來,一會(huì)我們要用。

3.configure(AuthenticationManagerBuilder auth)方法中用來配置我們的認(rèn)證方式,在auth.userDetailsService()方法中傳入userService,這樣userService中的loadUserByUsername方法在用戶登錄時(shí)將會(huì)被自動(dòng)調(diào)用。后面的passwordEncoder是可選項(xiàng),可寫可不寫,因?yàn)槲沂菍⒂脩舻拿魑拿艽a生成了MD5消息摘要后存入數(shù)據(jù)庫的,因此在登錄時(shí)也需要對(duì)明文密碼進(jìn)行處理,所以就加上了passwordEncoder,加上passwordEncoder后,直接new一個(gè)PasswordEncoder匿名內(nèi)部類即可,這里有兩個(gè)方法要實(shí)現(xiàn),看名字就知道方法的含義,第一個(gè)方法encode顯然是對(duì)明文進(jìn)行加密,這里我使用了MD5消息摘要,具體的實(shí)現(xiàn)方法是由commons-codec依賴提供的;第二個(gè)方法matches是密碼的比對(duì),兩個(gè)參數(shù),第一個(gè)參數(shù)是明文密碼,第二個(gè)是密文,這里只需要對(duì)明文加密后和密文比較即可(小伙伴如果對(duì)此感興趣可以繼續(xù)考慮密碼加鹽)。

4.configure(HttpSecurity http)用來配置我們的認(rèn)證規(guī)則等,authorizeRequests方法表示開啟了認(rèn)證規(guī)則配置,antMatchers("/admin/**").hasRole("超級(jí)管理員")表示 /admin/** 的路徑需要有‘超級(jí)管理員'角色的用戶才能訪問,我在網(wǎng)上看到小伙伴對(duì)hasRole方法中要不要加 ROLE_ 前綴有疑問,這里是不要加的,如果用hasAuthority方法才需要加。anyRequest().authenticated()表示其他所有路徑都是需要認(rèn)證/登錄后才能訪問。接下來我們配置了登錄頁面為login_page,登錄處理路徑為/login,登錄用戶名為username,密碼為password,并配置了這些路徑都可以直接訪問,注銷登陸也可以直接訪問,最后關(guān)閉csrf。在successHandler中,使用response返回登錄成功的json即可,切記不可以使用defaultSuccessUrl,defaultSuccessUrl是只登錄成功后重定向的頁面,使用failureHandler也是由于相同的原因。

5.configure(WebSecurity web)方法中我配置了一些過濾規(guī)則,不贅述。

6.另外,對(duì)于靜態(tài)文件,如 /images/** 、 /css/** 、 /js/** 這些路徑,這里默認(rèn)都是不攔截的。

Controller

最后來看看我們的Controller,如下:

@RestController
public class LoginRegController {

 /**
  * 如果自動(dòng)跳轉(zhuǎn)到這個(gè)頁面,說明用戶未登錄,返回相應(yīng)的提示即可
  * <p>
  * 如果要支持表單登錄,可以在這個(gè)方法中判斷請(qǐng)求的類型,進(jìn)而決定返回JSON還是HTML頁面
  *
  * @return
  */
 @RequestMapping("/login_page")
 public RespBean loginPage() {
  return new RespBean("error", "尚未登錄,請(qǐng)登錄!");
 }
}

這個(gè)Controller整體來說還是比較簡(jiǎn)單的,RespBean一個(gè)響應(yīng)bean,返回一段簡(jiǎn)單的json,不贅述,這里需要小伙伴注意的是 login_page ,我們配置的登錄頁面是一個(gè) login_page ,但實(shí)際上 login_page 并不是一個(gè)頁面,而是返回一段JSON,這是因?yàn)楫?dāng)我未登錄就去訪問其他頁面時(shí)Spring Security會(huì)自動(dòng)跳轉(zhuǎn)到到 login_page 頁面,但是在Ajax請(qǐng)求中,不需要這種跳轉(zhuǎn),我要的只是是否登錄的提示,所以這里返回json即可。

測(cè)試

最后小伙伴可以使用POSTMAN或者RESTClient等工具來測(cè)試登錄和權(quán)限問題,我就不演示了。

Ok,經(jīng)過上文的介紹,想必小伙伴們對(duì)Spring Boot+Spring Security處理Ajax登錄請(qǐng)求已經(jīng)有所了解了,好了,本文就說到這里,有問題歡迎留言討論。

相關(guān)文章

  • Java線程池ForkJoinPool實(shí)例解析

    Java線程池ForkJoinPool實(shí)例解析

    這篇文章主要介紹了Java線程池ForkJoinPool實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java匹配正則表達(dá)式匯總

    Java匹配正則表達(dá)式匯總

    java匹配字符串表達(dá)式在我們數(shù)據(jù)處理方面是及其重要的,現(xiàn)在就把我這幾天數(shù)據(jù)處理比較常用的向大家介紹一下,常規(guī)的一些匹配方式就不介紹了,我們來學(xué)習(xí)一些特殊的,感興趣的朋友跟隨小編一起看看吧
    2023-03-03
  • Java用Arrays.asList初始化ArrayList實(shí)例方法

    Java用Arrays.asList初始化ArrayList實(shí)例方法

    在本篇文章里小編給大家分享的是關(guān)于Java中使用Arrays.asList初始化ArrayList的知識(shí)點(diǎn)內(nèi)容,需要的朋友們參考下。
    2019-10-10
  • Kotlin 基礎(chǔ)教程之泛型

    Kotlin 基礎(chǔ)教程之泛型

    這篇文章主要介紹了Kotlin 基礎(chǔ)教程之泛型的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • SpringCloud如何利用Feign訪問外部http請(qǐng)求

    SpringCloud如何利用Feign訪問外部http請(qǐng)求

    這篇文章主要介紹了SpringCloud如何利用Feign訪問外部http請(qǐng)求,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java實(shí)現(xiàn)PDF轉(zhuǎn)為線性PDF詳解

    Java實(shí)現(xiàn)PDF轉(zhuǎn)為線性PDF詳解

    線性化PDF文件是PDF文件的一種特殊格式,可以通過Internet更快地進(jìn)行查看。本文將通過后端Java程序?qū)崿F(xiàn)將PDF文件轉(zhuǎn)為線性化PDF。感興趣的可以了解一下
    2021-12-12
  • 解讀@Scheduled任務(wù)調(diào)度/定時(shí)任務(wù)非分布式

    解讀@Scheduled任務(wù)調(diào)度/定時(shí)任務(wù)非分布式

    這篇文章主要介紹了解讀@Scheduled任務(wù)調(diào)度/定時(shí)任務(wù)非分布式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java中indexOf()的用法小結(jié)

    Java中indexOf()的用法小結(jié)

    這篇文章主要介紹了Java中indexOf()的用法小結(jié),indexOf()有四種方法,用來查找某個(gè)字符或字符串的位置,本文通過示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 淺談Java中的private方法是否可以被代理

    淺談Java中的private方法是否可以被代理

    這篇文章主要介紹了淺談Java中的private方法是否可以被代理,在?Java?8之前,接口可以有常量變量和抽象方法,我們不能在接口中提供方法實(shí)現(xiàn),如果我們要提供抽象方法和非抽象方法(方法與實(shí)現(xiàn))的組合,那么我們就得使用抽象類,需要的朋友可以參考下
    2023-12-12
  • 一次Spring項(xiàng)目打包問題排查的實(shí)戰(zhàn)記錄

    一次Spring項(xiàng)目打包問題排查的實(shí)戰(zhàn)記錄

    這篇文章主要給大家介紹了一次Spring項(xiàng)目打包問題排查的實(shí)戰(zhàn)記錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用Spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評(píng)論