首頁  >  文章  >  web前端  >  SpringBoot和SpringSecurity處理Ajax登入要求的問題

SpringBoot和SpringSecurity處理Ajax登入要求的問題

不言
不言原創
2018-06-25 10:31:171784瀏覽

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

最近在專案中遇到了這樣一個問題:前後端分離,前端用Vue來做,所有的資料請求都使用vue-resource,沒有使用表單,因此資料互動都是使用JSON,後台使用Spring Boot,權限驗證使用了Spring Security,因為之前用Spring Security都是處理頁面的,這次單純處理Ajax請求,因此記錄下遇到的一些問題。這裡的解決方案不僅適用於Ajax請求,也可以解決行動端請求驗證。

創建工程

首先我們需要建立一個Spring Boot工程,創建時需要引入Web、Spring Security、MySQL和MyBatis(資料庫框架其實隨意,我在這裡使用MyBatis),創建好之後,依賴檔案如下:

<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訊息摘要,我在後文中將對密碼進行簡單的處理。

建立資料庫並配置

為了簡化邏輯,我在這裡建立了三個表,分別是使用者表、角色表、使用者角色關聯表,如下:

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

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

建構實體類別

這裡主要是指建構使用者類,這裡的使用者類別比較特殊,必須實作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省略...
}

實作了UserDetails介面之後,該介面中有幾個方法需要我們實現,四個返回Boolean的方法都是見名知意,enabled表示檔期帳戶是否啟用,這個我數據庫中確實有該字段,因此根據查詢結果返回,其他的為了簡單期間都直接返回true,getAuthorities方法返回當前用戶的角色信息,用戶的角色其實就是roles中的數據,將roles中的數據轉換為Listd07df5a91c610ba2165cf20d64ff54b6之後返回即可, 這裡有一個要注意的地方,由於我在數據庫中存儲的角色名都是諸如'超級管理員'、'普通用戶'之類的,並不是以ROLE_ 這樣的字元開始的,因此需要在這裡手動加上ROLE_ ,切記。

另外還有一個Role實體類,比較簡單,依照資料庫的欄位建立即可,這裡不再贅述。

建立UserService

這裡的UserService也比較特殊,需要實作UserDetailsS​​ervice接口,如下:

##

@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;
 }
}

實作了UserDetailsS​​ervice介面之後,我們需要實作該介面中的loadUserByUsername方法,也就是根據使用者名稱查詢使用者。這裡注入了兩個MyBatis中的Mapper,UserMapper用來查詢用戶,RolesMapper用來查詢角色。在loadUserByUsername方法中,首先根據傳入的參數(參數就是用戶登入時輸入的用戶名)去查詢用戶,如果查到的用戶為null,可以直接拋一個UsernameNotFoundException異常,但我為了處理方便,回傳了一個沒有任何值的User對象,這樣在後面的密碼比對過程中一樣會發現登入失敗的(這裡大家根據自己的業務需求調整即可),如果查到的用戶不為null,此時我們根據查到的用戶id再去查詢該用戶的角色,並將查詢結果放入到user物件中,這個查詢結果會在user物件的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("超级管理员")
    .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,如下:

@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登录请求已经有所了解了,好了,本文就说到这里,有问题欢迎留言讨论

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

在Angular4中输入属性与输出属性(详细教程)

在JS中如何实现ajax和同源策略

在JS中如何实现ajax与ajax的跨域请求

以上是SpringBoot和SpringSecurity處理Ajax登入要求的問題的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn