在第一章我们是基于SpringSecurity、JWT技术实现前后端无状态化认证授权,而我们当前的项目是前后端分离的架构,同样也可借助Security框架和Jwt实现前后端的无状态认证授权操作;
1、项目自定义认证过滤器
1.1 依赖导入
在stock_backend工程导入SpringSecurity启动依赖:
<!--引入security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2 自定义认证过滤器
当前项目中认证登录信息的合法性,除了用户名、密码外,还需要校验验证码,所以认证过滤器需要注入redis模板对象:
新建com.itheima.stock.security.filter.JwtLoginAuthenticationFilter
package com.itheima.stock.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.stock.constant.StockConstant;
import com.itheima.stock.utils.JwtTokenUtil;
import com.itheima.stock.vo.req.LoginReqVo;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
*认证过滤器,核心作用:认证用户信息,并颁发令牌
*/
public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private RedisTemplate redisTemplate;
public JwtLoginAuthenticationFilter(String loginUrl) {
super(loginUrl);
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
//认证方法
//{"username":"itcast","password":"123456"}
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
//MediaType:请求的媒体类型
if (!request.getMethod().equalsIgnoreCase("POST") && MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(request.getContentType())) {
throw new AuthenticationServiceException("Authentication method not supported" + request.getMethod());
}
//获取前端ajax传入的数据流并反序列化为LoginReqVo对象
LoginReqVo reqVo = new ObjectMapper().readValue(request.getInputStream(), LoginReqVo.class);
//去redis中校验输入的验证码是否正确
String rCheckCode =(String) redisTemplate.opsForValue().get(StockConstant.CHECK_PREFIX + reqVo.getSessionId());
//设置响应格式和编码
response.setContentType("application/json;charset=utf-8");
if (rCheckCode==null || ! rCheckCode.equalsIgnoreCase(reqVo.getCode())) {
//响应验证码输入错误
R<Object> error = R.error(ResponseCode.CHECK_CODE_ERROR.getMessage());
//将error转换成json形式的字符串数据
response.getWriter().write(new ObjectMapper().writeValueAsString(error));
//返回null,不会进入下面的代码块,security会判断认证失败
return null;
}
String username = reqVo.getUsername();
username = (username != null) ? username.trim() : "";
String password = reqVo.getPassword();
password = (password != null) ? password : "";
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
//调用认证管理器进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
*
* @param request
* @param response
* @param chain 过滤器链
* @param authResult 认证对象
* method.
* @throws IOException
* @throws ServletException
*/
@Override
//认证成功时的回调方法
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
User user = (User) authResult.getPrincipal();
String username = user.getUsername();
Collection<GrantedAuthority> authorities = user.getAuthorities();
//生成token
String tokenStr = JwtTokenUtil.createToken(username, authorities.toString());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
Map<String,String> info = new HashMap<>();
info.put("msg","登陆成功");
info.put("code","1");
//返回token给前端
info.put("data",tokenStr);
response.getWriter().write(new ObjectMapper().writeValueAsString(info));
}
@Override
//认证失败时的回调方法
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
Map<String,String> info = new HashMap<>();
info.put("msg","登陆失败");
info.put("code","0");
info.put("data","");
response.getWriter().write(new ObjectMapper().writeValueAsString(info));
}
}