一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - SpringBoot+SpringSecurity處理Ajax登錄請求問題(推薦)

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

2021-03-08 12:19獨孤求敗 Java教程

這篇文章主要介紹了SpringBoot+SpringSecurity處理Ajax登錄請求問題,本文給大家介紹的非常不錯,具有參考借鑒價值,需要的朋友可以參考下

最近在項目中遇到了這樣一個問題:前后端分離,前端用vue來做,所有的數據請求都使用vue-resource,沒有使用表單,因此數據交互都是使用json,后臺使用spring boot,權限驗證使用了spring security,因為之前用spring security都是處理頁面的,這次單純處理ajax請求,因此記錄下遇到的一些問題。這里的解決方案不僅適用于ajax請求,也可以解決移動端請求驗證。

創建工程

首先我們需要創建一個spring boot工程,創建時需要引入web、spring security、mysql和mybatis(數據庫框架其實隨意,我這里使用mybatis),創建好之后,依賴文件如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<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>

注意最后一個 commons-codec 依賴是我手動加入進來的,這是一個apache的開源項目,可以用來生成md5消息摘要,我在后文中將對密碼進行簡單的處理。

創建數據庫并配置

為了簡化邏輯,我這里創建了三個表,分別是用戶表、角色表、用戶角色關聯表,如下:

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

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

?
1
2
3
spring.datasource.url=jdbc:mysql:///vueblog
spring.datasource.username=root
spring.datasource.password=123

構造實體類

這里主要是指構造用戶類,這里的用戶類比較特殊,必須實現userdetails接口,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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省略...
}

實現了userdetails接口之后,該接口中有幾個方法需要我們實現,四個返回boolean的方法都是見名知意,enabled表示檔期賬戶是否啟用,這個我數據庫中確實有該字段,因此根據查詢結果返回,其他的為了簡單期間都直接返回true,getauthorities方法返回當前用戶的角色信息,用戶的角色其實就是roles中的數據,將roles中的數據轉換為list<grantedauthority>之后返回即可, 這里有一個要注意的地方,由于我在數據庫中存儲的角色名都是諸如‘超級管理員'、‘普通用戶'之類的,并不是以 role_ 這樣的字符開始的,因此需要在這里手動加上 role_ ,切記 。

另外還有一個role實體類,比較簡單,按照數據庫的字段創建即可,這里不再贅述。

創建userservice

這里的userservice也比較特殊,需要實現userdetailsservice接口,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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,這里返回一個不含有任何值的user對象,在后期的密碼比對過程中一樣會驗證失敗
   return new user();
  }
  //查詢用戶的角色信息,并返回存入user中
  list<role> roles = rolesmapper.getrolesbyuid(user.getid());
  user.setroles(roles);
  return user;
 }
}

實現了userdetailsservice接口之后,我們需要實現該接口中的loaduserbyusername方法,即根據用戶名查詢用戶。這里注入了兩個mybatis中的mapper,usermapper用來查詢用戶,rolesmapper用來查詢角色。在loaduserbyusername方法中,首先根據傳入的參數(參數就是用戶登錄時輸入的用戶名)去查詢用戶,如果查到的用戶為null,可以直接拋一個usernamenotfoundexception異常,但是我為了處理方便,返回了一個沒有任何值的user對象,這樣在后面的密碼比對過程中一樣會發現登錄失敗的(這里大家根據自己的業務需求調整即可),如果查到的用戶不為null,此時我們根據查到的用戶id再去查詢該用戶的角色,并將查詢結果放入到user對象中,這個查詢結果將在user對象的getauthorities方法中用上。

security配置

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@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("超級管理員")
    .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.首先這是一個配置類,因此記得加上@configuration注解,又因為這是spring security的配置,因此記得繼承websecurityconfigureradapter。

2.將剛剛創建好的userservice注入進來,一會我們要用。

3.configure(authenticationmanagerbuilder auth)方法中用來配置我們的認證方式,在auth.userdetailsservice()方法中傳入userservice,這樣userservice中的loaduserbyusername方法在用戶登錄時將會被自動調用。后面的passwordencoder是可選項,可寫可不寫,因為我是將用戶的明文密碼生成了md5消息摘要后存入數據庫的,因此在登錄時也需要對明文密碼進行處理,所以就加上了passwordencoder,加上passwordencoder后,直接new一個passwordencoder匿名內部類即可,這里有兩個方法要實現,看名字就知道方法的含義,第一個方法encode顯然是對明文進行加密,這里我使用了md5消息摘要,具體的實現方法是由commons-codec依賴提供的;第二個方法matches是密碼的比對,兩個參數,第一個參數是明文密碼,第二個是密文,這里只需要對明文加密后和密文比較即可(小伙伴如果對此感興趣可以繼續考慮密碼加鹽)。

4.configure(httpsecurity http)用來配置我們的認證規則等,authorizerequests方法表示開啟了認證規則配置,antmatchers("/admin/**").hasrole("超級管理員")表示 /admin/** 的路徑需要有‘超級管理員'角色的用戶才能訪問,我在網上看到小伙伴對hasrole方法中要不要加 role_ 前綴有疑問,這里是不要加的,如果用hasauthority方法才需要加。anyrequest().authenticated()表示其他所有路徑都是需要認證/登錄后才能訪問。接下來我們配置了登錄頁面為login_page,登錄處理路徑為/login,登錄用戶名為username,密碼為password,并配置了這些路徑都可以直接訪問,注銷登陸也可以直接訪問,最后關閉csrf。在successhandler中,使用response返回登錄成功的json即可,切記不可以使用defaultsuccessurl,defaultsuccessurl是只登錄成功后重定向的頁面,使用failurehandler也是由于相同的原因。

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

6.另外,對于靜態文件,如 /images/**/css/**/js/** 這些路徑,這里默認都是不攔截的。

controller

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@restcontroller
public class loginregcontroller {
 
 /**
  * 如果自動跳轉到這個頁面,說明用戶未登錄,返回相應的提示即可
  * <p>
  * 如果要支持表單登錄,可以在這個方法中判斷請求的類型,進而決定返回json還是html頁面
  *
  * @return
  */
 @requestmapping("/login_page")
 public respbean loginpage() {
  return new respbean("error", "尚未登錄,請登錄!");
 }
}

這個controller整體來說還是比較簡單的,respbean一個響應bean,返回一段簡單的json,不贅述,這里需要小伙伴注意的是 login_page ,我們配置的登錄頁面是一個 login_page ,但實際上 login_page 并不是一個頁面,而是返回一段json,這是因為當我未登錄就去訪問其他頁面時spring security會自動跳轉到到 login_page 頁面,但是在ajax請求中,不需要這種跳轉,我要的只是是否登錄的提示,所以這里返回json即可。

測試

最后小伙伴可以使用postman或者restclient等工具來測試登錄和權限問題,我就不演示了。

ok,經過上文的介紹,想必小伙伴們對spring boot+spring security處理ajax登錄請求已經有所了解了,好了,本文就說到這里,有問題歡迎留言討論。

原文鏈接:https://segmentfault.com/a/1190000012476796?utm_source=tuicool&utm_medium=referral

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美另类videos另类粗暴 | 日韩高清一区二区三区不卡 | 国产精品视频免费视频 | 成年人免费观看视频网站 | 欧美精品v日韩精品v国产精品 | 国产精品毛片久久久久久久 | 天天视频官网天天视频在线 | 美女一线天 | 美国雪白人妖sarina | 青春草在线观看精品免费视频 | 十大免费b2b网站 | 好吊色青青青国产综合在线观看 | 成人小视频在线观看免费 | 亚洲国产精品嫩草影院久久 | 日韩拍拍拍 | www射com| 精品一二三区久久AAA片 | 免费一级特黄特色大片在线 | 国语第一次处破女 | 色倩网站 | 国色天香视频资源网 | 免费观看无遮挡www的小视频 | 东北疯狂xxxxbbbb中国 | 青青青国产在线观看 | 日本xxxx69hd| 99综合在线| 亚洲va在线va天堂va偷拍 | 国产欧美一区视频在线观看 | 欧美日韩免费一区二区在线观看 | 麻豆网站在线看 | 帅老头恋帅老头同性tv | 亚洲男女在线 | 成人影院免费看 | 免费在线视频成人 | 日本肉体xxxx | 国内揄拍国内精品久久 | 日本高清不卡一区久久精品 | 国产成人一区二区三区小说 | 含羞草传媒一天免费看下 | 日韩精品高清自在线 | 亚洲天堂导航 |