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

*/

@Configuration

public 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的配置链中去,

@Override

protected 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 过滤器链

代码

@Component

public 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

回到顶部