SpringBootSecurity前后端分离登录验证的实现
最近⼀段时间在研究OAuth2的使⽤,想整个单点登录,从⽹上了很多demo都没有实施成功,也许是因为压根就不懂OAuth2的原理导致。有那么⼏天,越来越没有头绪,⼜不能放弃,转过头来⼀想,OAuth2是在Security的基础上扩展的,对于Security⾃⼰都是⼀⽆所知,⼲脆就先研究⼀下Security吧,先把Security搭建起来,感觉。
说⼲就⼲,在现成的SpringBoot 2.1.4.RELEASE环境下,进⾏Security的使⽤。
简单的Security的使⽤就不说了,⽬前的项⽬是前后端分离的,登录成功或者失败返回的数据格式必须JSON形式的,未登录时也需要返回JSON格式的提⽰信息,退出时⼀样需要返回JSON格式的数据。授权先不管,先返回JSON格式的数据,这⼀个搞定,也研究了好⼏天,翻看了很多别⼈的经验,别⼈的经验有的看得懂,有的看不懂,关键时刻还需要⾃⼰研究呀。
下⾯,上代码:
第⼀步,在l中引⼊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 t.annotation.Bean;
import t.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.fig.annotation.web.builders.HttpSecurity;
import org.fig.annotation.web.builders.WebSecurity;
import org.fig.figuration.WebSecurityConfigurerAdapter;
import org.userdetails.UserDetailsService;
import org.userdetails.UsernameNotFoundException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 参考⽹址:
* blog.csdn/XlxfyzsFdblj/article/details/82083443
* blog.csdn/lizc_lizc/article/details/84059004
* blog.csdn/XlxfyzsFdblj/article/details/82084183
* blog.csdn/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 = Writer();
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 = Writer();
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 = Writer();
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 = Writer();
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 = Writer();
out.write(objectMapper.writeValueAsString(map));
out.flush();
out.close();
})
.
permitAll();
//开启跨域访问
//开启模拟请求,⽐如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.userdetails.UserDetails;
import org.userdetails.UserDetailsService;
import org.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.pto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* ⾃定义的密码加密⽅法,实现了PasswordEncoder接⼝
* @author 程就⼈⽣
*
*/
@Component
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
//加密⽅法可以根据⾃⼰的需要修改
String();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return encode(charSequence).equals(s);
}
}
说明:这个类主要是对密码加密的处理,以及⽤户传递过来的密码和数据库密码(UserDetailsService中的密码)进⾏⽐对。
第五步,实现UserDetails接⼝
import java.util.Collection;
import org.GrantedAuthority;
import org.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<? extends GrantedAuthority> authorities;
public void setUsername(String username) {
this.username = username;
}
springboot原理pdfpublic void setPassword(String password) {
this.password = password;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public Collection<? 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) Context().getAuthentication() .getPrincipal();
代码写完了,接下来需要测试⼀下,经过测试才能证明代码的有效性,先⽤浏览器吧。
第⼀步测试,未登录前访问index,页⾯直接重定向到默认的login页⾯了,测试接⼝OK。
图-1第⼆步测试,登录login后,返回了json数据,测试结果OK。
图-2第三步测试,访问index,返回输出的登录数据,测试结果OK。
图-3
第四步,访问logout,返回json数据,测试接⼝OK。
图-4
第五步,⽤API POST测试,⽤这个⼯具模拟ajax请求,看请求结果如何,⾸先访问index,这个必须登录后才能访问。测试结果ok,返回了我们需要的JSON格式数据。
图-5
第六步,在登录模拟对话框,设置环境变量,以保持登录状态。
图-6
**第七步,登录测试,返回JSON格式的数据,测试结果OK。
图-7第⼋步,在返回到index测试窗⼝,发送请求,返回当前⽤户JSON格式的信息,测试结果OK。
图-8第九步,测试退出,返回JSON格式数据,测试结果OK

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。