1.springsecurity是一个安全管理框架,底层实现为一条过滤器链,就是用户请求进来,判断有没有请求的权限,抛出异常,重定向跳转。
首先引入security的依赖
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
中间过程中验证请求头中是否存在登录信息(token)
验证登录信息是否正确的业务可以实现UserDetailsService接口重写loadUserByUsername方法
public class UserDetailsServiceImpl implements UserDetailsService {
@Qualifier("com.zhang.demo.service.GateWayService")
@Autowired
private GateWayService gateWayService;
@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
//进入UserDetailsService过滤链检验登录信息是否为真
if(null==userName||"".equals(userName)){
System.err.println(userName);
throw new RuntimeException("未登录");
}
User loginUser = null;
try {
loginUser = gateWayService.findUserByUserName();
}catch (Exception e){
//可抛出异常用于捕获
System.err.println("报错了");
}
//校验是否存在
if(Objects.isNull(loginUser)){
System.err.println("用户名或密码错误");
throw new RuntimeException("用户名或密码错误");
}
//通过返回
return new LoginUser(loginUser);
}
}
编写SecurityConfig配置类暴露校验接口以及配置跨域和放行请求
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//Result是个人编写的结果集
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
//暴露
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//暴露接口用来编码和校验密码
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//放行请求
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/call/codeImg", "/call/getToken", "/call/findAllBranch", "/user/user/findUserByUserName");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//自定以登录页面
//http.formLogin().loginPage("/login");
http.headers().cacheControl();// 不要缓存
http.cors();// 允许跨域
http.csrf().disable();//关闭csrf
//匿名访问
http.authorizeRequests().antMatchers("/call/login", "/call/getToken").anonymous()
.antMatchers("/**").authenticated()//需要认证
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//不通过session获取securitycontext
.and()
.authorizeRequests()
//初上名外的所有请求全部需要鉴权认证
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(
// 没有权限的处理
new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json");
httpServletResponse.setHeader("charatar", "UTF-8");
Result result = new Result(500, "您没有该权限");
httpServletResponse.getWriter().println(JSONObject.toJSONString(result));
httpServletResponse.getWriter().flush();
}
})
.authenticationEntryPoint(new AuthenticationEntryPoint() {
// 没有登录的时候的处理
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setContentType("application/json");
httpServletResponse.setHeader("charatar", "UTF-8");
Result result = new Result(501, "您尚未登录");
httpServletResponse.getWriter().println(JSONObject.toJSONString(result));
httpServletResponse.getWriter().flush();
}
});
//添加jwt的过滤器到校验登录信息的过滤器之前
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
校验请求头中是否携带Jwt生成的登录信息
如果存在就放行到下一个过滤器进行过滤操作,反之就不通过
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private RedisTemplate redisTemplate;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
String token = httpServletRequest.getHeader("token");
// System.err.println("token-----"+token);
// System.err.println(token==null);
// System.err.println(token);
//判断请求投中是否有登录信息
if(token==null||"null".equals(token)||"".equals(token)){
//放行到下一个内置过滤器
filterChain.doFilter(httpServletRequest,httpServletResponse);
//返回到此
return;
}
//此处是对请求头中的token进行解密操作如若不了解可查看博客中的Jwt生成
DecodedJWT tokenInfo = JwtUtil.getTokenInfo(token);
//get()获取存放的时候存放的数据
String loginUser = tokenInfo.getClaims().get("loginUser").asString();
User user = JSONObject.parseObject(loginUser, User.class);
if(!redisTemplate.hasKey("LOGIN_USER"+user.getId())){
throw new RuntimeException("未登录");
}
//存入SecurityContextHolder
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, null, null);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
}
登录接口进行登录成功,把登录信息生成一个Jwt的token以后发送到前端,然后吧这条登录的token保存起来(列vuex,或存储到本地),之后每次向后端发送请求的时候都要携带这个token,到后台要对这个token进行解析并与缓存、数据库中的数据进行对比判断是否认证或授权。