温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

SpringBoot Security实现前后端分离登录验证

发布时间:2020-11-03 18:36:16 来源:亿速云 阅读:587 作者:Leah 栏目:开发技术

本篇文章为大家展示了SpringBoot Security实现前后端分离登录验证,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

第一步,在pom.xml中引入Security配置文件

<dependency>
 <groupId>org.springframework.boot</groupId>  
 <artifactId>spring-boot-starter-security</artifactId>  
</dependency>

第二步,增加Configuration配置文件

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 参考网址:
 * https://blog.csdn.net/XlxfyzsFdblj/article/details/82083443
 * https://blog.csdn.net/lizc_lizc/article/details/84059004
 * https://blog.csdn.net/XlxfyzsFdblj/article/details/82084183
 * https://blog.csdn.net/weixin_36451151/article/details/83868891
 * 查找了很多文件,有用的还有有的,感谢他们的辛勤付出
 * Security配置文件,项目启动时就加载了
 * @author 程就人生
 *
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {  
 
 @Autowired
 private MyPasswordEncoder myPasswordEncoder;
 
 @Autowired
 private UserDetailsService myCustomUserService;
 
 @Autowired
 private ObjectMapper objectMapper;

 @Override
 protected void configure(HttpSecurity http) throws Exception {
 
  http
  .authenticationProvider(authenticationProvider())
  .httpBasic()
  //未登录时,进行json格式的提示,很喜欢这种写法,不用单独写一个又一个的类
   .authenticationEntryPoint((request,response,authException) -> {
    response.setContentType("application/json;charset=utf-8");
    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    PrintWriter out = response.getWriter();
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("code",403);
    map.put("message","未登录");
    out.write(objectMapper.writeValueAsString(map));
    out.flush();
    out.close();
   })
   
   .and()
   .authorizeRequests()
   .anyRequest().authenticated() //必须授权才能范围
   
   .and()
   .formLogin() //使用自带的登录
   .permitAll()
   //登录失败,返回json
   .failureHandler((request,response,ex) -> {
    response.setContentType("application/json;charset=utf-8");
    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    PrintWriter out = response.getWriter();
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("code",401);
    if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
     map.put("message","用户名或密码错误");
    } else if (ex instanceof DisabledException) {
     map.put("message","账户被禁用");
    } else {
     map.put("message","登录失败!");
    }
    out.write(objectMapper.writeValueAsString(map));
    out.flush();
    out.close();
   })
   //登录成功,返回json
   .successHandler((request,response,authentication) -> {
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("code",200);
    map.put("message","登录成功");
    map.put("data",authentication);
    response.setContentType("application/json;charset=utf-8");
    PrintWriter out = response.getWriter();
    out.write(objectMapper.writeValueAsString(map));
    out.flush();
    out.close();
   })
   .and()
   .exceptionHandling()
   //没有权限,返回json
   .accessDeniedHandler((request,response,ex) -> {
    response.setContentType("application/json;charset=utf-8");
    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
    PrintWriter out = response.getWriter();
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("code",403);
    map.put("message", "权限不足");
    out.write(objectMapper.writeValueAsString(map));
    out.flush();
    out.close();
   })
   .and()
   .logout()
   //退出成功,返回json
   .logoutSuccessHandler((request,response,authentication) -> {
    Map<String,Object> map = new HashMap<String,Object>();
    map.put("code",200);
    map.put("message","退出成功");
    map.put("data",authentication);
    response.setContentType("application/json;charset=utf-8");
    PrintWriter out = response.getWriter();
    out.write(objectMapper.writeValueAsString(map));
    out.flush();
    out.close();
   })
   .permitAll();
   //开启跨域访问
   http.cors().disable();
   //开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
   http.csrf().disable();
 }
 
 @Override
 public void configure(WebSecurity web) {
  //对于在header里面增加token等类似情况,放行所有OPTIONS请求。
  web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
 }

 @Bean
 public AuthenticationProvider authenticationProvider() {
  DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
  //对默认的UserDetailsService进行覆盖
  authenticationProvider.setUserDetailsService(myCustomUserService);
  authenticationProvider.setPasswordEncoder(myPasswordEncoder);
  return authenticationProvider;
 }
 
}

第三步,实现UserDetailsService接口

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
/**
 * 登录专用类
 * 自定义类,实现了UserDetailsService接口,用户登录时调用的第一类
 * @author 程就人生
 *
 */
@Component
public class MyCustomUserService implements UserDetailsService {

 /**
  * 登陆验证时,通过username获取用户的所有权限信息
  * 并返回UserDetails放到spring的全局缓存SecurityContextHolder中,以供授权器使用
  */
 @Override
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  //在这里可以自己调用数据库,对username进行查询,看看在数据库中是否存在
  MyUserDetails myUserDetail = new MyUserDetails();
  myUserDetail.setUsername(username);
  myUserDetail.setPassword("123456");
  return myUserDetail;
 }
}

说明:这个类,主要是用来接收登录传递过来的用户名,然后可以在这里扩展,查询该用户名在数据库中是否存在,不存在时,可以抛出异常。本测试为了演示,把数据写死了。

第四步,实现PasswordEncoder接口

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
 * 自定义的密码加密方法,实现了PasswordEncoder接口
 * @author 程就人生
 *
 */
@Component
public class MyPasswordEncoder implements PasswordEncoder {

 @Override
 public String encode(CharSequence charSequence) {
  //加密方法可以根据自己的需要修改
  return charSequence.toString();
 }

 @Override
 public boolean matches(CharSequence charSequence, String s) {
  return encode(charSequence).equals(s);
 }
}

说明:这个类主要是对密码加密的处理,以及用户传递过来的密码和数据库密码(UserDetailsService中的密码)进行比对。

第五步,实现UserDetails接口

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

/**
 * 实现了UserDetails接口,只留必需的属性,也可添加自己需要的属性
 * @author 程就人生
 *
 */
@Component
public class MyUserDetails implements UserDetails {

 /**
  * 
  */
 private static final long serialVersionUID = 1L;

 //登录用户名
 private String username;
 //登录密码
 private String password;

 private Collection<&#63; extends GrantedAuthority> authorities;

 public void setUsername(String username) {
  this.username = username;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public void setAuthorities(Collection<&#63; extends GrantedAuthority> authorities) {
  this.authorities = authorities;
 }

 @Override
 public Collection<&#63; extends GrantedAuthority> getAuthorities() {
  return this.authorities;
 }

 @Override
 public String getPassword() {
  return this.password;
 }

 @Override
 public String getUsername() {
  return this.username;
 }

 @Override
 public boolean isAccountNonExpired() {
  return true;
 }

 @Override
 public boolean isAccountNonLocked() {
  return true;
 }

 @Override
 public boolean isCredentialsNonExpired() {
  return true;
 }

 @Override
 public boolean isEnabled() {
  return true;
 }
}

说明:这个类是用来存储登录成功后的用户数据,登录成功后,可以使用下列代码获取:

MyUserDetails myUserDetails= (MyUserDetails) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();

代码写完了,接下来需要测试一下,经过测试才能证明代码的有效性,先用浏览器吧。

第一步测试,未登录前访问index,页面直接重定向到默认的login页面了,测试接口OK。

SpringBoot Security实现前后端分离登录验证

图-1

第二步测试,登录login后,返回了json数据,测试结果OK。

SpringBoot Security实现前后端分离登录验证

图-2

第三步测试,访问index,返回输出的登录数据,测试结果OK。

SpringBoot Security实现前后端分离登录验证

图-3

第四步,访问logout,返回json数据,测试接口OK。

SpringBoot Security实现前后端分离登录验证

图-4

第五步,用API POST测试,用这个工具模拟ajax请求,看请求结果如何,首先访问index,这个必须登录后才能访问。测试结果ok,返回了我们需要的JSON格式数据。

SpringBoot Security实现前后端分离登录验证

图-5

第六步,在登录模拟对话框,设置环境变量,以保持登录状态。

SpringBoot Security实现前后端分离登录验证

图-6

**第七步,登录测试,返回JSON格式的数据,测试结果OK。

SpringBoot Security实现前后端分离登录验证

图-7

第八步,在返回到index测试窗口,发送请求,返回当前用户JSON格式的信息,测试结果OK。

SpringBoot Security实现前后端分离登录验证

图-8

第九步,测试退出,返回JSON格式数据,测试结果OK

SpringBoot Security实现前后端分离登录验证

图-9

第十步,退出后,再访问index,出现问题,登录信息还在,LOOK!

SpringBoot Security实现前后端分离登录验证

图-10

把头部的header前面的勾去掉,也就是去掉cookie,这时正常了,原因很简单,在退出时,没有清除cookie,这个只能到正式的环境上去测了。API POST再怎么模拟还是和正式环境有区别的。

如果在API POST测试报403错误,那就需要把configuration配置文件里的

//开启跨域访问
http.cors().disable();
//开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
http.csrf().disable();

 到此这篇关于SpringBoot Security前后端分离登录验证的实现的文章就介绍到这了,更多相关SpringBoot Security登录验证内容请搜索亿速云以前的文章或继续浏

上述内容就是SpringBoot Security实现前后端分离登录验证,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI