辛苦搞了2周终于搞出来了!遇到各种问题百度各中资料,各种尝试终于不辜负啊。
- 主要功能:用户输入用户名、密码、短信验证码,后台校验用户名、密码和手机验证码后登录成功。
- 技术栈:使用spring security框架搭建并继承 (我没有使用xml配置模式看别人有用xml文件配置的,应该是异曲同工吧)
不多废话,正文附源码,疑问可评论。
1.继承WebSecurityConfigureAdapter的类:WebApplicationSecurityConfig
网上这个类的说明一抓一大把我也就是照猫画虎的本事,关注点是在本类的初始化JwtAuthenticationProvider在内的provider对象时很容易犯错,也要注意相同的addFilter、addFilterBefore、addFilterAfter等各类过滤器也有先后顺序。
2.继承UsernamePasswordAuthenticationFilter的类:JwtLoginFilter(主要验证码逻辑位置)
3.继承UsernamePasswordAuthenticationToken的类:JwtAuthenticationToken
4.继承DaoAuthenticationProvider的类:JwtAuthenticationProvider
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationProvider extends DaoAuthenticationProvider {
public JwtAuthenticationProvider(JdbcUserDetailsServiceImpl userDetailsService) {
super();
setHiderUserNotFoundException(false);
setUserDetailsService(userDetailsService);
}
public JwtAuthenticationProvider() {
super();
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//手机号和密码登录可是使用下列逻辑直接认证,我是账号密码再家短信验证码所以此处逻辑还是账号密码逻辑无需变更
// JwtCodeAuthenticationToken authenticationToken = (JwtCodeAuthenticationToken) authentication;
// Object principal = authentication.getPrincipal();// 获取凭证也就是用户的手机号
// String phone = "";
// if (principal instanceof String) {
// phone = (String) principal;
// }
//
// String inputCode = (String) authentication.getCredentials(); // 获取输入的验证码
//
// Integer cacheObject = redisCache.getCacheObject("login"+phone);
//
// // 1. 检验Redis手机号的验证码
// if (cacheObject == null) {
// throw new BadCredentialsException("验证码已经过期或尚未发送,请重新发送验证码");
// }
// if (!inputCode.equals(cacheObject+"")) {
// throw new BadCredentialsException("输入的验证码不正确,请重新输入");
// }
// // 2. 根据手机号查询用户信息
// UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(phone);
// if (userDetails == null) {
// throw new InternalAuthenticationServiceException("phone用户不存在,请注册");
// }
// // 3. 重新创建已认证对象,
// JwtCodeAuthenticationToken authenticationResult = new JwtAuthenticationToken(userDetails, inputCode, userDetails.getAuthorities());
// authenticationResult.setDetails(authenticationToken.getDetails());
// return authenticationResult;
return super.authenticate(authentication);
}
@Override
public boolean supports(Class<?> aClass) {
return JwtAuthenticationToken.class.isAssignableFrom(aClass);
}
}
分割线
ackage com.seahorse.youliao.security;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* @Description: 自定义令牌对象
* @author:songqiang
* @Date:2020-01-10 11:28
**/
public class JwtAuthenticatioToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = 1L;
private String token;
public JwtAuthenticatioToken(Object principal, Object credentials){
super(principal, credentials);
}
public JwtAuthenticatioToken(Object principal, Object credentials, String token){
super(principal, credentials);
this.token = token;
}
public JwtAuthenticatioToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(principal, credentials, authorities);
}
public JwtAuthenticatioToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, String token) {
super(principal, credentials, authorities);
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.context.support.MessageSourceAccessor;
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
// 自定义对象校验短信验证码是否正确
private static final MobileService mobileService =ContextHolder.getbean(MobileService.class);
//
@Autowired
private SessionRegistry sessionRegistry;
public JwtLoginFilter(AuthenticationManager authManager,
AuthenticationSuccessHandler successHandler,
AuthenticationFailureHandler failureHandler,
ApplicationEventPublisher eventPublisher,
MessageSourceAccessor messages,
SessionRegistry sessionRegistry) {
setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login","POST"));
setAuthenticationManager(authManager);
setAuthenticationSuccessHandler(successHandler);
setAuthenticationFailureHandler(failureHandler);
setApplicationEventPublisher(eventPublisher);
this.sessionRegistry=sessionRegistry;
//messages用于各项验证码失败类抛出异常处理
this.messages=messages;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// POST 请求 /login 登录时拦截, 由此方法触发执行登录认证流程,可以在此覆写整个登录认证逻辑
super.doFilter(req, res, chain);
}
/**
* 此过滤器的用户名密码默认从request.getParameter()获取
* 在此做验证码的验证
* @param request
* @param response
* @return
* @throws AuthenticationException
*/
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username=request.getParameter("username");
String password=request.getParameter("password");
String message=request.getParameter("message");
String phone=request.getParameter("phone");
//账户和密码
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
//先验证用户名和密码
JwtAuthenticatioToken authRequest=new JwtAuthenticatioToken(authRequest);
this.setDetails(request,authRequest);
authentication auth=getAuthenticationManager().authenticate(authRequest);
if(phone)
{
boolean status=mobileService.checkMessage(username,phone,message);
if (status==false)
{
RedirectStrategy redirect=new DefaultRedirectStrategy();
//messages用于各项验证码失败类抛出异常的翻译器
request.getSession(true).setAttribute("SPRING_SECURITY_LAST_EXCEPTION",new Exception(messages.getMessage("JwtAuthenticationProvider.credentialsExpired")));
redirect.setRedirect(request,response,"/login.jsp?error=404");
return null;
}
}
SecurityContextHolder.getContext().setAuthentication(auth);
String hello=request.getSession().getId();
sessionRegistry.registerNewSession(hello,auth.getPrincipal());
return auth;
}
protected void setDetails(HttpServletRequest request,JwtAuthenticatioToken authRequest)
{
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}