Heim  >  Artikel  >  Web-Frontend  >  Probleme mit SpringBoot und SpringSecurity bei der Verarbeitung von Ajax-Anmeldeanfragen

Probleme mit SpringBoot und SpringSecurity bei der Verarbeitung von Ajax-Anmeldeanfragen

不言
不言Original
2018-06-25 10:31:171785Durchsuche

Dieser Artikel stellt hauptsächlich das Problem der Verarbeitung von Ajax-Anmeldeanfragen durch SpringBoot + SpringSecurity vor. Dieser Artikel stellt Sie sehr gut vor und hat Referenzwert.

Ich bin kürzlich im Projekt darauf gestoßen Ein Problem: Das Front-End wird mit Vue durchgeführt und es werden keine Formulare verwendet. Daher wird im Hintergrund JSON verwendet . Spring Security wird zur Berechtigungsüberprüfung verwendet. Da Spring Security dieses Mal nur Ajax-Anfragen verarbeitet, werden einige aufgetretene Probleme aufgezeichnet. Die Lösung hier eignet sich nicht nur für Ajax-Anfragen, sondern löst auch die Überprüfung mobiler Anfragen.

Projekt erstellen

Zuerst müssen wir ein Spring Boot-Projekt erstellen. Beim Erstellen müssen wir Web, Spring Security, MySQL usw. einführen MyBatis (Datenbank-Framework Eigentlich verwende ich hier MyBatis. Nach der Erstellung lautet die Abhängigkeitsdatei wie folgt:

<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>

Beachten Sie die letzten Commons -codec Abhängigkeit ist, dass ich sie manuell hinzugefügt habe. Dies ist ein Apache-Open-Source-Projekt, das zum Generieren von MD5-Nachrichtenauszügen verwendet werden kann. Ich werde das Passwort im folgenden Artikel kurz verarbeiten.

Datenbank erstellen und konfigurieren

Um die Logik zu vereinfachen, habe ich hier drei Tabellen erstellt, nämlich Benutzertabelle, Rollentabelle und Benutzerrolle Die Zuordnungstabelle lautet wie folgt:

Als nächstes müssen wir einfach unsere Datenbank in application.properties konfigurieren. Hier kommt es auf Ihre spezifische Situation an.

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

Entitätsklassen erstellen

Bezieht sich hier hauptsächlich auf die Erstellung von Benutzerklassen, hier Die Die Benutzerklasse ist etwas Besonderes und muss die UserDetails-Schnittstelle wie folgt implementieren:

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省略...
}

Nach der Implementierung der UserDetails-Schnittstelle müssen wir in dieser Schnittstelle mehrere Methoden implementieren Implementieren, vier Die Methoden, die einen Booleschen Wert zurückgeben, geben an, ob das Zeitplankonto in meiner Datenbank vorhanden ist, sodass es der Einfachheit halber direkt zurückgegeben wird Die getAuthorities-Methode gibt die Rolle des aktuellen Benutzers zurück. Die Rolle des Benutzers besteht lediglich darin, die Daten in Rollen umzuwandeln und sie dann zurückzugeben Alle in der Datenbank gespeicherten Dateien wie „Superadministratoren“, „normale Benutzer“ und dergleichen beginnen nicht mit Zeichen wie ROLE_, daher müssen Sie ROLE_ hier manuell hinzufügen. Denken Sie daran.

Es gibt auch eine Rollenentitätsklasse, die relativ einfach ist und entsprechend den Feldern der Datenbank erstellt werden kann. Ich werde hier nicht näher darauf eingehen.

UserService erstellen

Der UserService hier ist ebenfalls etwas Besonderes und muss die UserDetailsService-Schnittstelle wie folgt implementieren:

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

Nach der Implementierung der UserDetailsService-Schnittstelle müssen wir die Methode „loadUserByUsername“ in dieser Schnittstelle implementieren, dh den Benutzer basierend auf dem Benutzernamen abfragen. Hier werden zwei Mapper in MyBatis eingefügt: UserMapper wird zum Abfragen von Benutzern und RolesMapper zum Abfragen von Rollen verwendet. Fragen Sie in der Methode „loadUserByUsername“ zunächst den Benutzer anhand der eingehenden Parameter ab (der Parameter ist der Benutzername, der beim Anmelden des Benutzers eingegeben wird). Wenn der gefundene Benutzer null ist, können Sie zur Vereinfachung der Verarbeitung direkt eine UsernameNotFoundException-Ausnahme auslösen Ich habe ein A-Benutzerobjekt ohne Wert zurückgegeben, sodass der Anmeldefehler beim anschließenden Kennwortvergleich immer noch gefunden wird (Sie können ihn entsprechend Ihren eigenen Geschäftsanforderungen anpassen, wenn der gefundene Benutzer zu diesem Zeitpunkt nicht null ist). Wir werden es anhand der Benutzer-ID finden und dann die Rolle des Benutzers abfragen und die Abfrageergebnisse in das Benutzerobjekt einfügen. Dieses Abfrageergebnis wird in der getAuthorities-Methode des Benutzerobjekts verwendet.

Sicherheitskonfiguration

Werfen wir zunächst einen Blick auf meine Sicherheitskonfiguration, dann erkläre ich sie einzeln:

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

Das ist der Kern unserer Konfiguration. Freunde, hört mir zu:

1 @Configuration hinzufügen Hinweis: Da dies die Konfiguration von Spring Security ist, denken Sie daran, WebSecurityConfigurerAdapter zu erben.

2. Fügen Sie den soeben erstellten UserService ein, wir werden ihn später verwenden.

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的跨域请求

Das obige ist der detaillierte Inhalt vonProbleme mit SpringBoot und SpringSecurity bei der Verarbeitung von Ajax-Anmeldeanfragen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn