1.导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
2.编写配置类
在config文件夹下创建MySpringSecurityConfiguration作为本项目对于SpringSecurity框架的配置类, 并添加@Configuration注解。
继承框架WebSecurityConfigurerAdapter并重写两个configure方法。(以绑定自定义的认证和过滤器)
@Autowired UserDetailsService mySecurityService;@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(mySecurityService) // 绑定自定义的认证Service .passwordEncoder(new BCryptPasswordEncoder()); // 绑定密码处理器 } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/doc.html", "/doc.html/**", "/webjars/**", "/v2/**", "/swagger-resources", "/swagger-resources/**", "/swagger-ui.html", "/swagger-ui.html/**").permitAll() .anyRequest().authenticated() .and() // 设置跨域的处理 .cors().configurationSource(corsConfigurationSource()) .and() .addFilter(new TokenLoginFilter(super.authenticationManager())) // 绑定认证的接口(将步骤三定义的登录过滤器扔进来) .addFilter(new TokenVerifyFilter(super.authenticationManager())) // 绑定校验的接口(将步骤四定义的token校验过滤器扔进来) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }
自定义认证业务类继承SpringSecurity框架的UserDetailsService并重写loadUserByUsername(此方法完成账号的校验)
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1.需要根据账号查询 List<SysUser> list = sysUserService.queryByUserName(username); if(list != null && list.size() == 1){ // 账号是存在的 SysUser sysUser = list.get(0); // 根据当前登录的账号查询到关联的角色信息 List<SysRole> sysRoles = sysRoleService.queryByUserId(sysUser.getUserId()); List<GrantedAuthority> listRole = new ArrayList<>(); if(sysRoles != null && sysRoles.size() > 0){ for (SysRole sysRole : sysRoles) { //绑定当前用户所关联的角色 listRole.add(new SimpleGrantedAuthority(sysRole.getRoleName())); } } // 密码模拟的是就数据库查询出来 return new User(sysUser.getUsername(),sysUser.getPassword(),listRole); } return null; }
3.重写登录过滤器
在filter文件夹中建过滤器类,例:UserNamePassWordLoginFilter,继承SpringSecurity框架的UsernamePasswordAuthenticationFilter
private AuthenticationManager authenticationManager; public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager){ this.authenticationManager = authenticationManager; } /** * 具体认证的方法 * @param request * @param response * @return * @throws AuthenticationException */ @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { SysUser sysUser = null; // 前后端分离的项目中我们提交的数据是JSON字符串。不是表单提交的 try { String loginInfo = getRequestJSON(request); sysUser = JSON.parseObject(loginInfo, SysUser.class); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUsername(),sysUser.getPassword()); return authenticationManager.authenticate(authenticationToken); } catch (IOException e) { e.printStackTrace(); } return null; } private String getRequestJSON(HttpServletRequest request) throws IOException { BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream())); StringBuilder sb = new StringBuilder(); String inputStr = null; while((inputStr = streamReader.readLine() ) != null){ sb.append(inputStr); } return sb.toString(); } /** * 登录成功的方法 * @param request * @param response * @param chain * @param authResult * @throws IOException * @throws ServletException */ @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response , FilterChain chain, Authentication authResult) throws IOException, ServletException { // 生成Token信息 Map<String,String> map = new HashMap<>(); map.put("username",authResult.getName()); // 生成对应的Token信息 String token = JWTUtils.getToken(map); // 需要把生成的Token信息响应给客户端 response.addHeader("Authorization", SystemConstant.SYS_TOKEN_PREFIX +token); response.addHeader("Access-Control-Expose-Headers","Authorization"); response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter writer = response.getWriter(); Map<String,Object> resultMap = new HashMap<>(); resultMap.put("code", HttpServletResponse.SC_OK); resultMap.put("msg","认证通过"); writer.write(JSON.toJSONString(resultMap)); writer.flush(); writer.close(); } /** * 登录失败的方法 * @param request * @param response * @param failed * @throws IOException * @throws ServletException */ @Override protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter writer = response.getWriter(); Map<String,Object> resultMap = new HashMap<>(); resultMap.put("code", HttpServletResponse.SC_UNAUTHORIZED); resultMap.put("msg","用户名或密码错误!"); writer.write(JSON.toJSONString(resultMap)); writer.flush(); writer.close(); }
4.校验提交的Token
在filter文件夹中建过滤器类,例:TokenBasicAuthenticationFilter,继承SpringSecurity框架的BasicAuthenticationFilter
private AuthenticationManager authenticationManager;public TokenBasicAuthenticationFilter(AuthenticationManager authenticationManager) { super(authenticationManager); } /** * 校验提交的Token是否合法的方法 * @param request * @param response * @param chain * @throws IOException * @throws ServletException */ @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("--->"+request.getRequestURI()); // 获取请求携带的Token信息 String header = request.getHeader("Authorization"); String requestURI = request.getRequestURI(); String contextPath = request.getContextPath(); String path = requestURI.replace(contextPath,""); List<String> msgs = Arrays.asList("/doc.html","/webjars","/v2","/v3","/favicon.ico","swagger-resources"); for (String p : msgs) { if(path.contains(p)){ // 放过请求 chain.doFilter(request,response); return ; } } System.out.println("request.getContextPath() = " + request.getContextPath()); if(header != null && header.startsWith(SystemConstant.SYS_TOKEN_PREFIX)){ // 传递了Token信息。同时有我们添加的对应的前缀 // 1.获取到正常的token String token = header.replace(SystemConstant.SYS_TOKEN_PREFIX, ""); // 2.校验token信息是否合法 DecodedJWT verify = JWTUtils.verify(token); if(verify == null){ responseLogin(response); } // 走到这儿说明是正常 // 获取当前登录的账号信息 String userName = verify.getClaim("username").asString(); // 放过请求 查找权限 List<GrantedAuthority> list = authenticationManager.getAuthentication(userName); // 根据账号获取相关的权限 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName,"",list); SecurityContextHolder.getContext().setAuthentication(authenticationToken); // 放过请求 chain.doFilter(request,response); }else{ // 没有携带Token或者是非法的请求 responseLogin(response); } } private void responseLogin(HttpServletResponse response) throws IOException { // 说明校验失败 -- 给用户提示请先登录 response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter writer = response.getWriter(); Map<String,Object> resultMap = new HashMap<>(); resultMap.put("code", HttpServletResponse.SC_FORBIDDEN); resultMap.put("msg","请先登录!"); writer.write(JSON.toJSONString(resultMap)); writer.flush(); writer.close(); }