Spring Boot + Security实现简单验证登录操作
利用spring security 实现简单的登陆验证,并且在登陆失败或者成功后进行对前端返回的处理。
1.准备(数据库配置等)
本例子使用的Mysql + Hibernate
image2.引入maven依赖
本例子需要的依赖有
基本的mysql,jpa,还有spring security的oauth2,jwt
image3.新建表User,UserRole
创建entity:User和UserRole,在本例子中实际上只有User一个表就够了,毕竟只是验证用户名和密码嘛,但是我习惯每次创建User就手痒价格Role的表。
2个表多对多的关系也在代码中有用Hibernate写了,感兴趣的可以在Git上看一下。
/**
* Store User information.
*/
@Entity
@Table(name ="sys_user")
public class User {
private StringuserName;
private StringuserDescription;
private Stringpassword;
private Listroles;
@Id
@Column(name ="user_name")
public StringgetUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Column(name ="user_desc")
public StringgetUserDescription() {
return userDescription;
}
public void setUserDescription(String userDescription) {
this.userDescription = userDescription;
}
@Column(name ="password")
public StringgetPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@ManyToMany
@JoinTable(name ="sys_user_role",
joinColumns =@JoinColumn(name ="user_name", referencedColumnName ="user_name", updatable =false, insertable =false),
inverseJoinColumns =@JoinColumn(name ="role_code", referencedColumnName ="role_code", updatable =false, insertable =false))
public ListgetRoles() {
return roles;
}
public void setRoles(List roles) {
this.roles = roles;
}
}
4.User Repository
此处只需要添加两个方法,findByUserName在UserDetialService load user的信息时候用,一个是测试用。
@Repository
public interface UserRepositoryextends JpaRepository {
UserfindByUserName(String userName);
@Query(value ="select r.roleCode from User u inner join u.roles as r where u.userName = :userName")
ListqueryUserOwnedRoleCodes(@Param(value ="userName") String userName);
}
5.新建DatabaseUserDetailsService
新建DatabaseUserDetailsService继承UserDetailsService,并重写loadUserByUsername方法,在用户登陆时,spring会调用这个方法去获得user的信息(密码等),以对比页面传过来的用户名和密码是否正确。
@Override
public UserDetailsloadUserByUsername(String userName)throws UsernameNotFoundException {
User user =userRepository.findByUserName(userName);
if (user ==null) {
//throw exception inform front end not this user
throw new UsernameNotFoundException("user + " + userName +"not found.");
}
List roleCodeList =userRepository.queryUserOwnedRoleCodes(userName);
List authorities =
roleCodeList.stream().map(e ->new SimpleGrantedAuthority(e)).collect(Collectors.toList());
UserDetails userDetails =new org.springframework.security.core.userdetails.User(
user.getUserName(),user.getPassword(),authorities);
return userDetails;
}
6.新建WebSecuerityConfig
建立一个WebSecuerityConfig类继承WebSecurityConfigurerAdapter,并重写两个configure方法,
配置各种访问权限限制以及添加处理类
(1)不需要限制的用permitAll()放行即可。
(2).successHandler() 和 .failureHandler() 是配置登录失败或成功时的处理,后面有写这两个类的实现。
(3).authenticationEntryPoint()是没有登录就请求资源时的处理。
@Override
public void configure(HttpSecurity http)throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().csrf().disable()
.authorizeRequests()
.antMatchers("/v2/api-docs/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginProcessingUrl("/api/login")
.successHandler(successHandler)
.failureHandler(failHandler)
.and().exceptionHandling().authenticationEntryPoint(entryPoint);
}
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception {
auth.userDetailsService(userDetailsService);
}
7.登陆失败或成功的处理。
未登录就请求资源时,spring会交给AuthenticationEntryPoint处理。
登陆成功之后,spring会跳到AuthenticationFailHandler。
登陆失败之后,spring会跳到AuthenticationSuccessHandler。
所以我们要继承这两个方法,把想要返回给页面的信息在这两个类中写一下。
@Service("authenticationEntryPointImpl")
public class AuthenticationEntryPointImplimplements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, AuthenticationException e)throws IOException {
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
}
}
@Service("authenticationSuccessHandler")
public class AuthenticationSuccessHandlerextends SavedRequestAwareAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response
, Authentication authentication)throws IOException {
logger.info("User: " + request.getParameter("username") +" Login successfully.");
this.returnJson(response);
}
private void returnJson(HttpServletResponse response)throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"200\"," +
"\"message\": \"Login successfully.\",\"serverTime\": " + System.currentTimeMillis() +"}");
}
}
@Service("authenticationFailHandler")
public class AuthenticationFailHandlerextends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)throws IOException, ServletException {
this.returnJson(response,exception);
}
private void returnJson(HttpServletResponse response,
AuthenticationException exception)throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println("{\"exceptionId\":\"null\",\"messageCode\":\"401\"," +
"\"message\": \""+ exception.getMessage() +"\",\"serverTime\": " + System.currentTimeMillis() +"}");
}
}
8.Postman 测试
这是我数据库中存在的数据
image没有登录直接发送普通时:
image密码或用户名输入错误时,
image用户名密码都正确时:
image