springboot集成springsecurity实现json串登录和短信验证码登录(1)
问题:用spring security 实现json串登录方式一般用来解决前后端分离的登录问题的处理,前端通过输入用户名密码json串发送后端由security验证登录,登录成功返回登录成功标识token。以后请求只需带token即可通过验证。这其中涉及到几个问题:
1.如何让spring security 校验我们自定义的json串登录过滤器
2.登录成功后,后续请求如何让spring security 验证token来实现自动认证
那么,解决这两个问题,首先得看spring security登录的实现方式,spring security实现登录是通过一系列过滤器链来最终来完成登录,所以我们需要自定义一个json登录和校验过滤器加入到security的过滤器链。而且我们通过spring security 的UsernamePasswordAuthenticationFilter.class源码
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
} else {
String username = this.obtainUsername(request);
String password = this.obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
发现我们只需将用户名密码传给UsernamePasswordAuthenticationToken类并调用UsernamePasswordAuthenticationFilter的this.getAuthenticationManager().authenticate(authRequest)方法即可实现框架的自动认证
首先我们需要定义一个Json 用户名密码登录配置器
/** * Json 用户名密码登录配置文件(配置器) * * @author liaofuxing * @date 2020/02/18 11:50 */@Configurationpublic class JsonAuthenticationConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {@Autowired
private AuthenticationSuccessHandler defaultAuthenticationSuccessHandler;
@Autowired
private AuthenticationFailureHandler defaultAuthenticationFailureHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
JsonAuthenticationFilter jsonAuthenticationFilter = new JsonAuthenticationFilter();
jsonAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
jsonAuthenticationFilter.setAuthenticationSuccessHandler(defaultAuthenticationSuccessHandler);
jsonAuthenticationFilter.setAuthenticationFailureHandler(defaultAuthenticationFailureHandler);
http.addFilterAfter(jsonAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
}
设置登录成功失败的Handler,登录成功Handler里面实现成功表示token的返回,具体代码稍后在gitee中查看,这里就不一一列出,和验证登录的过滤器jsonAuthenticationFilter,
并模仿UsernamePasswordAuthenticationFilter自定义JsonAuthenticationFilter过滤器
/** * Json 用户名密码登录过滤器* * @author liaofuxing * @date 2020/02/18 11:50 */public class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private boolean postOnly = true;
public JsonAuthenticationFilter() {
super(new AntPathRequestMatcher("/user/login", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setExpandEntityReferences(false);
StringBuffer sb = new StringBuffer();
try (InputStream inputStream = request.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
String str;
while ((str = bufferedReader.readLine()) != null) {
sb.append(str);
}
} catch (IOException ex) {
throw new RuntimeException("获取请求内容异常", ex);
}
JSONObject jsonObject = JSON.parseObject(sb.toString());
String username = jsonObject.getString("username");
String password = jsonObject.getString("password");
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
return this.getAuthenticationManager().authenticate(authenticationToken);
}
}
与UsernamePasswordAuthenticationFilter一样继承AbstractAuthenticationProcessingFilter重写attemptAuthentication方法实现框架的自动认证
ps:UsernamePasswordAuthenticationFilter的自动登录认证是通过定义的UserDetailServiceImpl来实现用户名密码校验的,所以先要定义好UserDetailServiceImpl 和User实体类。这些都是先决条件。
将我们定义的JsonAuthenticationConfigurer 添加到spring security的配置链中去,
@Overrideprotected void configure(HttpSecurity http) throws Exception {//处理跨域请求
http.cors().and().csrf().disable()
.apply(jsonAuthenticationConfigurer)
.and()
.apply(springSocialConfigurer)
.and()
.apply(smsCodeAuthenticationConfigurer)
.and()
//权限不足结果处理
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler)
.and()
//设置登出url
.logout().logoutUrl("/user/logout")
//设置登出成功处理器(下面介绍)
.logoutSuccessHandler(logoutSuccessHandler).and()
.authorizeRequests()
.antMatchers("/authentication/require",
"/sms/*",
"/user/regist").permitAll()
.antMatchers("/user/lala/**").hasRole("ADMIN")
.anyRequest()
.authenticated();
/* authorizationFilter是用来拦截登录请求判断请求中是否带有token,并且token是否有对应的已经登录的用户,如果有应该直接授权通过
* 所以这个过滤器应该在UsernamePasswordAuthenticationFilter过滤器之前执行,所以放在LogoutFilter之后
*/
http.addFilterAfter(authorizationFilter, LogoutFilter.class);
}
这个是完整配置,jsonAuthenticationConfigurer是我们添加进去的。
这样就实现了json串形式的登录,解决了问题1,
问题2,实现后续请求的token校验,同样是定义过滤器,添加到security 过滤器链
代码
@Componentpublic class TokenAuthorizationFilter extends OncePerRequestFilter {@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private UserDetailServiceImpl userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//从请求头中取出token
String token = request.getHeader("token");
if(!StringUtils.isEmpty(token)) {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
//从redis中获取用户名
String username = redisTemplate.opsForValue().get("SECURITY_TOKEN:"+ token);
//从数据库中根据用户名获取用户
UserDetails systemUser = userDetailsService.loadUserByUsername(username);
if (systemUser != null) {
//解析并设置认证信息
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(systemUser, null, systemUser.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
以上只是部分代码,主要是梳理流程,具体代码:spring cloud学习实例代码
代码在 api-gateway 这个项目中。
项目过滤器示意图。
以上是 springboot集成springsecurity实现json串登录和短信验证码登录(1) 的全部内容, 来源链接: utcz.com/z/514105.html