“在昨天的文章中,我们使用Spring Boot实现了一个Security的应用,但是其中登录的方式和密码都是框架中自带的,今天我们来看一下如何自定义登录认证”
首先说几个Spring Security的最核心组件(自认为):WebSecurityConfigurerAdapter;UserDetails;UserDetailsService;第一个类是自带的权限控制类,通过重写它的方法来设置一些web安全的细节,UserDetails是自带的实体类,UserDetailsService是自带的用户认证的类(主要通过它来实现自定义用户登录认证)。
01
—
首先我们来定义User实体类:
@Component
public class UserVO implements UserDetails,Serializable {
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;
public void setUsername(String username) {
this.username = username;
}
public 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;
}
}
然后是Service,这里的用户密码和用角色默认写死,大家可以根据实际情况去数据库中去取。
@Component
public class MyUserService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserVO myUserDetails = new UserVO();
//这里username就是我们表单中用户名,security在加载的时候会将值传到这里
myUserDetails.setUsername(username);
//模拟从数据库取出的密码
myUserDetails.setPassword(new BCryptPasswordEncoder().encode("12345"));
//模拟从数据库取出的权限
HashSet<SimpleGrantedAuthority> set = new HashSet<>();
// set.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
//角色名称
set.add(new SimpleGrantedAuthority("ROLE_USER"));
myUserDetails.setAuthorities(set);
return myUserDetails;
}
}
最后是权限控制了:这里重写了configuer方法(共有三个,这里重写了其中两个),注意参数的不同。AjaxResponseBody是一个封装的对象,定义了状态和信息
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserService myUserService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().permitAll()
.and()
.formLogin().loginPage("/login")
//这个URL比较特殊, Security自带无需定义
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
System.out.println("登录成功");
AjaxResponseBody responseBody = new AjaxResponseBody();
responseBody.setStatus("200");
responseBody.setMsg("Login Success!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody));
}
})
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.println("登录失败");
AjaxResponseBody responseBody = new AjaxResponseBody();
responseBody.setStatus("400");
responseBody.setMsg("Login Failure!");
httpServletResponse.getWriter().write(JSON.toJSONString(responseBody));
}
})
.permitAll()
.and()
.csrf().disable();//允许跨域
}
//自定义认证方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserService).passwordEncoder(new BCryptPasswordEncoder());
}
}
到这里我们就大功告成了,启动完用postman测试一下:
1.使用正确密码
2.使用错误密码
自定义登录认证到这里就实现了,也解决了昨天的第一个问题。那么第二个问题使用RESTful架构设计,如何实现权限认证呢?
其实通过上面我们就可以看到,自定义权限认证之后,登录成功和失败都会返回数据给客户端,当我们使用Restful架构时,我们可以在成功登录之后返回Token给客户端,这个Token保存用户信息,在后面请求的时候我们先判断Token是否合法,不合法走登录逻辑,合法我们去判断他的权限(有些请求等级低的用户无法请求),具体的实现我们留到下一篇文章。