前后端分离与分布式环境下,session不好使了,这时要着重使用令牌了
这时利用框架提供的两个父类
UsernamePasswordAuthenticationFilter 用于登录验证
BasicAuthenticationFilter 用于令牌验证
登录验证过滤器 UsernamePasswordAuthenticationFilter
/*** 认证过滤器** @author YUDI* @date 2020/4/10*/public class LoginFilter extends UsernamePasswordAuthenticationFilter {private AuthenticationManager authenticationManager;private RsaKeyProperties prop;public LoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) {this.authenticationManager = authenticationManager;this.prop = prop;// /login为内置接口this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST"));}/*** 处理登录请求的方法** @param request* @param response* @return* @throws AuthenticationException*/@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {try {//将前端传入的参数转换成实体类对象User user = new ObjectMapper().readValue(request.getInputStream(), User.class);//账号密码登录 将访问UserDetailsService的方法UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());//认证通过return authenticationManager.authenticate(authRequest);} catch (Exception e) {try {response.setContentType("application/json;charset=utf-8");//没有认证的编码response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);PrintWriter out = response.getWriter();Map<String, Object> resultMap = new HashMap<>(2);resultMap.put("code", HttpServletResponse.SC_UNAUTHORIZED);resultMap.put("msg", "用户名或密码错误");out.write(new ObjectMapper().writeValueAsString(resultMap));out.flush();out.close();} catch (Exception outEx) {throw new RuntimeException(outEx);}throw new RuntimeException(e);}}/*** 认证成功之后执行的方法(在响应头里颁发令牌)** @param request* @param response* @param chain* @param authResult* @throws IOException* @throws ServletException*/@Overridepublic void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {User user = new User();user.setUsername(authResult.getName());//设置权限user.setPermissions((List<Permission>) authResult.getAuthorities());//设置令牌有效期 时间单位为分钟String token = JwtUtils.generateTokenExpireInMinutes(user, prop.getPrivateKey(), 24 * 60);response.addHeader("Authorization", "Bearer " + token);try {response.setContentType("application/json;charset=utf-8");//没有认证的编码response.setStatus(HttpServletResponse.SC_OK);PrintWriter out = response.getWriter();Map<String, Object> resultMap = new HashMap<>();resultMap.put("code", HttpServletResponse.SC_OK);resultMap.put("msg", "认证通过");out.write(new ObjectMapper().writeValueAsString(resultMap));out.flush();out.close();} catch (Exception outEx) {outEx.printStackTrace();}}}
令牌过滤器 BasicAuthenticationFilter
/**
* 验证认证过滤器
*
* @author YUDI
* @date 2020/4/10
*/
public class JwtVerifyFilter extends BasicAuthenticationFilter {
private RsaKeyProperties prop;
public JwtVerifyFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) {
super(authenticationManager);
this.prop = prop;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
//如果携带错误格式的token,则给用户提示请登录!
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
PrintWriter out = response.getWriter();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
resultMap.put("msg", "没有登录或权限不够");
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
chain.doFilter(request, response);
} else {
//如果携带了正确格式的token要先得到token
String token = header.replace("Bearer ", "");
//验证token是否正确
Payload<User> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), User.class);
//根据令牌得到用户信息
User user = payload.getUserInfo();
if (user != null) {
UsernamePasswordAuthenticationToken authResult = new UsernamePasswordAuthenticationToken(user.getUsername(), token, user.getAuthorities());
//授权
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
} else {
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("msg", "token有误");
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
chain.doFilter(request, response);
}
}
}
}
security配置类
在这个配置类里 配置前面两个过滤器,如果作为资源服务则只需要配置令牌过滤器即可
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsServiceImpl userDetailsService;
@Autowired
private RsaKeyProperties prop;
//配置BCrypt加密方案
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
//配置用户信息和权限
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//使用数据库动态添加
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
//配置拦截请求资源
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
//关闭打开的csrf保护
http.csrf().disable();
//如何权限控制 给每一条路径分配一个权限名称 账号只要管理该名称 就可以访问权限
//url含有order的需要认证才能访问 fullyAuthenticated方法
http.authorizeRequests()
.antMatchers("/order/**").fullyAuthenticated()
.and()
//配置过滤器
.addFilter(new LoginFilter(super.authenticationManager(), prop))
.addFilter(new JwtVerifyFilter(super.authenticationManager(), prop))
//禁用session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
