当前位置:   article > 正文

Springboot整合SpringSecurity

Springboot整合SpringSecurity

一.Spring Security介绍

Spring Security是基于Spring生态圈的,用于提供安全访问控制解决方案的框架。Spring Security的安 全管理有两个重要概念,分别是Authentication(认证)和Authorization(授权)。 为了方便Spring Boot项目的安全管理,Spring Boot对Spring Security安全框架进行了整合支持,并提 供了通用的自动化配置,从而实现了Spring Security安全框架中包含的多数安全管理功能。

Spring Security登录认证主要涉及两个重要的接口 UserDetailService和UserDetails接口。

UserDetailService接口主要定义了一个方法 loadUserByUsername(String username)用于完成用户信息的查 询,其中username就是登录时的登录名称,登录认证时,需要自定义一个实现类实现UserDetailService接 口,完成数据库查询,该接口返回UserDetail。

UserDetail主要用于封装认证成功时的用户信息,即UserDetailService返回的用户信息,可以用Spring

自己的User对象,但是最好是实现UserDetail接口,自定义用户对象。

二.Spring Security认证步骤

1. 自定UserDetails类:当实体对象字段不满足时需要自定义UserDetails,一般都要自定义

UserDetails。

2. 自定义UserDetailsService类,主要用于从数据库查询用户信息。

3. 创建登录认证成功处理器,认证成功后需要返回JSON数据,菜单权限等。

4. 创建登录认证失败处理器,认证失败需要返回JSON数据,给前端判断。

5. 创建匿名用户访问无权限资源时处理器,匿名用户访问时,需要提示JSON。

6. 创建认证过的用户访问无权限资源时的处理器,无权限访问时,需要提示JSON。

7. 配置Spring Security配置类,把上面自定义的处理器交给Spring Security。

三.Spring Security认证实现

3.1添加Spring Security依赖

在pom.xml文件中添加Spring Security核心依赖,代码如下所

  1. <dependency>
  2.    <groupId>org.springframework.boot</groupId>
  3.    <artifactId>spring-boot-starter-security</artifactId>
  4. </dependency>

  3.2自定义UserDetails

   当实体对象字段不满足时Spring Security认证时,需要自定义UserDetails。

   1. 将User类实现UserDetails接口

   2. 将原有的isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired和isEnabled属性修 改成boolean类型,同时添加authorities属性。

  1. @Data
  2. @TableName("sys_user")
  3. public class User implements Serializable, UserDetails {
  4.    
  5.    //省略原有的属性......
  6.    
  7.    /**
  8.     * 帐户是否过期(1 未过期,0已过期)
  9.     */
  10.    private boolean isAccountNonExpired = true;
  11.    /**
  12.     * 帐户是否被锁定(1 未过期,0已过期)
  13.     */
  14.    private boolean isAccountNonLocked = true;
  15.    /**
  16.     * 密码是否过期(1 未过期,0已过期)
  17.     */
  18.    private boolean isCredentialsNonExpired = true;
  19.    /**
  20.     * 帐户是否可用(1 可用,0 删除用户)
  21.     */
  22.    private boolean isEnabled = true;
  23.    /**
  24.     * 权限列表
  25.     */
  26.    @TableField(exist = false)
  27.    Collection<? extends GrantedAuthority> authorities;

3.3.编写Service接口

 

  1. public interface UserService extends IService<User> {
  2.    /**
  3.     * 根据用户名查询用户信息
  4.     * @param userName
  5.     * @return
  6.     */
  7.    User findUserByUserName(String userName);
  8. }

 

3.4.编写ServiceImpl

  1. package com.manong.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.manong.entity.User;
  4. import com.manong.dao.UserMapper;
  5. import com.manong.service.UserService;
  6. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  7. import org.springframework.stereotype.Service;
  8. import org.springframework.transaction.annotation.Transactional;
  9. /**
  10. * <p>
  11. * 服务实现类
  12. * </p>
  13. *
  14. * @author lemon
  15. * @since 2022-12-06
  16. */
  17. @Service
  18. @Transactional
  19. public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
  20. @Override
  21. public User findUserByUserName(String username) {
  22. //创建条件构造器对象
  23. QueryWrapper queryWrapper=new QueryWrapper();
  24. queryWrapper.eq("username",username);
  25. //执行查询
  26. return baseMapper.selectOne(queryWrapper);
  27. }
  28. }

3.5. 自定义UserDetailsService类

  1. package com.manong.config.security.service;
  2. import com.manong.entity.Permission;
  3. import com.manong.entity.User;
  4. import com.manong.service.PermissionService;
  5. import com.manong.service.UserService;
  6. import org.springframework.security.core.GrantedAuthority;
  7. import org.springframework.security.core.authority.AuthorityUtils;
  8. import org.springframework.security.core.userdetails.UserDetails;
  9. import org.springframework.security.core.userdetails.UserDetailsService;
  10. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  11. import org.springframework.stereotype.Component;
  12. import javax.annotation.Resource;
  13. import java.util.List;
  14. import java.util.Objects;
  15. import java.util.stream.Collectors;
  16. /*
  17. * 用户认证处理器类
  18. * */
  19. @Component
  20. public class CustomerUserDetailService implements UserDetailsService {
  21. @Resource
  22. private UserService userService;
  23. @Resource
  24. private PermissionService permissionService;
  25. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
  26. //根据对象查找用户信息
  27. User user = userService.findUserByUserName(username);
  28. //判断对象是否为空
  29. if(user==null){
  30. throw new UsernameNotFoundException("用户的账号密码错误");
  31. }
  32. //查询当前登录用户拥有权限列表
  33. List<Permission> permissionList = permissionService.findPermissionListByUserId(user.getId());
  34. //获取对应的权限编码
  35. List<String> codeList = permissionList.stream()
  36. .filter(Objects::nonNull)
  37. .map(item -> item.getCode())
  38. .filter(Objects::nonNull)
  39. .collect(Collectors.toList());
  40. //将权限编码转换成数据
  41. String [] strings=codeList.toArray(new String[codeList.size()]);
  42. //设置权限列表
  43. List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(strings);
  44. //将权限列表设置给User
  45. user.setAuthorities(authorityList);
  46. //设置该用户拥有的菜单
  47. user.setPermissionList(permissionList);
  48. //查询成功
  49. return user;
  50. }
  51. }

四.通常情况下,我们需要自定义四个类来获取处理类

包括成功,失败,匿名用户,登录了但没有权限的用户

4.1.成功

  1. package com.manong.config.security.handler;
  2. import com.alibaba.fastjson.serializer.SerializerFeature;
  3. import com.manong.entity.User;
  4. import com.manong.utils.JwtUtils;
  5. import com.manong.utils.LoginResult;
  6. import com.manong.utils.ResultCode;
  7. import io.jsonwebtoken.Jwts;
  8. import org.springframework.security.core.Authentication;
  9. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  10. import org.springframework.stereotype.Component;
  11. import sun.net.www.protocol.http.AuthenticationHeader;
  12. import javax.annotation.Resource;
  13. import javax.servlet.ServletException;
  14. import javax.servlet.ServletOutputStream;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.io.IOException;
  18. import java.nio.charset.StandardCharsets;
  19. import com.alibaba.fastjson.JSON;
  20. /*
  21. * 登录认证成功处理器类
  22. * */
  23. @Component
  24. public class LoginSuccessHandler implements AuthenticationSuccessHandler {
  25. @Resource
  26. private JwtUtils jwtUtils;
  27. @Override
  28. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  29. //设置相应编码格式
  30. response.setContentType("application/json;charset-utf-8");
  31. //获取当前登录用户的信息
  32. User user = (User) authentication.getPrincipal();
  33. //创建token对象
  34. String token = jwtUtils.generateToken(user);
  35. //设置token的秘钥和过期时间
  36. long expireTime = Jwts.parser()
  37. .setSigningKey(jwtUtils.getSecret())
  38. .parseClaimsJws(token.replace("jwt_", ""))
  39. .getBody().getExpiration().getTime();//设置过期时间
  40. //创建LOgin登录对象
  41. LoginResult loginResult=new LoginResult(user.getId(), ResultCode.SUCCESS,token,expireTime);
  42. //将对象转换成json格式
  43. //消除循环引用
  44. String result = JSON.toJSONString(loginResult, SerializerFeature.DisableCircularReferenceDetect);
  45. //获取输出流
  46. ServletOutputStream outputStream = response.getOutputStream();
  47. outputStream.write(result.getBytes(StandardCharsets.UTF_8));
  48. outputStream.flush();
  49. outputStream.close();
  50. }
  51. }

4.2 失败

  1. package com.manong.config.security.handler;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializerFeature;
  4. import com.baomidou.mybatisplus.extension.api.R;
  5. import com.manong.entity.User;
  6. import com.manong.utils.Result;
  7. import com.manong.utils.ResultCode;
  8. import org.springframework.security.authentication.*;
  9. import org.springframework.security.core.AuthenticationException;
  10. import org.springframework.security.web.authentication.AuthenticationFailureHandler;
  11. import org.springframework.stereotype.Component;
  12. import javax.servlet.ServletException;
  13. import javax.servlet.ServletOutputStream;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. import java.nio.charset.StandardCharsets;
  18. @Component
  19. public class LoginFailureHandler implements AuthenticationFailureHandler {
  20. @Override
  21. public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  22. //设置相应编码格式
  23. response.setContentType("application/json;charset-utf-8");
  24. //获取输出流
  25. ServletOutputStream outputStream = response.getOutputStream();
  26. //定义变量,保存异常信息
  27. String message=null;
  28. //判断异常类型
  29. if(exception instanceof AccountExpiredException){
  30. message="账户过期失败";
  31. }
  32. else if(exception instanceof BadCredentialsException){
  33. message="用户名的账号密码错误,登录失败";
  34. }
  35. else if(exception instanceof CredentialsExpiredException){
  36. message="密码过期,登录失败";
  37. }
  38. else if(exception instanceof DisabledException){
  39. message="账号过期,登录失败";
  40. }
  41. else if(exception instanceof LockedException){
  42. message="账号被上锁,登录失败";
  43. }
  44. else if(exception instanceof InternalAuthenticationServiceException){
  45. message="用户不存在";
  46. }
  47. else {
  48. message="登录失败";
  49. }
  50. //将结果转换为Json格式
  51. String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message));
  52. //将结果保存到输出中
  53. outputStream.write(result.getBytes(StandardCharsets.UTF_8));
  54. outputStream.flush();
  55. outputStream.close();
  56. }
  57. }

 4.3 匿名无用户

  1. package com.manong.config.security.handler;
  2. import com.alibaba.fastjson.serializer.SerializerFeature;
  3. import com.manong.entity.User;
  4. import com.manong.utils.Result;
  5. import com.manong.utils.ResultCode;
  6. import org.springframework.security.access.AccessDeniedException;
  7. import org.springframework.security.core.Authentication;
  8. import org.springframework.security.core.AuthenticationException;
  9. import org.springframework.security.web.AuthenticationEntryPoint;
  10. import org.springframework.security.web.access.AccessDeniedHandler;
  11. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  12. import org.springframework.stereotype.Component;
  13. import sun.net.www.protocol.http.AuthenticationHeader;
  14. import javax.servlet.ServletException;
  15. import javax.servlet.ServletOutputStream;
  16. import javax.servlet.http.HttpServletRequest;
  17. import javax.servlet.http.HttpServletResponse;
  18. import java.io.IOException;
  19. import java.nio.charset.StandardCharsets;
  20. import com.alibaba.fastjson.JSON;
  21. /*
  22. * 匿名访问无权限资源处理器
  23. * */
  24. @Component
  25. public class AnonymousAuthenticationHandler implements AuthenticationEntryPoint {
  26. @Override
  27. public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
  28. response.setContentType("application/json;charset-utf-8");
  29. //获取输出流
  30. ServletOutputStream outputStream = response.getOutputStream();
  31. //将对象转换成json格式
  32. //消除循环引用
  33. String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("匿名用户无权限访问"), SerializerFeature.DisableCircularReferenceDetect);
  34. //获取输出流
  35. outputStream.write(result.getBytes(StandardCharsets.UTF_8));
  36. outputStream.flush();
  37. outputStream.close();
  38. }
  39. }

 4.4 登录了但是没有权限的用户

  1. package com.manong.config.security.handler;
  2. import com.alibaba.fastjson.serializer.SerializerFeature;
  3. import com.manong.entity.User;
  4. import com.manong.utils.Result;
  5. import com.manong.utils.ResultCode;
  6. import org.springframework.security.access.AccessDeniedException;
  7. import org.springframework.security.core.Authentication;
  8. import org.springframework.security.web.access.AccessDeniedHandler;
  9. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  10. import org.springframework.stereotype.Component;
  11. import sun.net.www.protocol.http.AuthenticationHeader;
  12. import javax.servlet.ServletException;
  13. import javax.servlet.ServletOutputStream;
  14. import javax.servlet.http.HttpServletRequest;
  15. import javax.servlet.http.HttpServletResponse;
  16. import java.io.IOException;
  17. import java.nio.charset.StandardCharsets;
  18. import com.alibaba.fastjson.JSON;
  19. /*
  20. * 认证用户访问无权限资源处理器
  21. * */
  22. @Component
  23. public class CustomerAccessDeniedHandler implements AccessDeniedHandler {
  24. @Override
  25. public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
  26. response.setContentType("application/json;charset-utf-8");
  27. ServletOutputStream outputStream = response.getOutputStream();
  28. //将对象转换成json格式
  29. //消除循环引用
  30. String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("用户无权限访问,请联系教务处"), SerializerFeature.DisableCircularReferenceDetect);
  31. //获取输出流
  32. outputStream.write(result.getBytes(StandardCharsets.UTF_8));
  33. outputStream.flush();
  34. outputStream.close();
  35. }
  36. }

五.编写SpringSecurity配置类,把上面的四个类进行合并

  1. package com.manong.config.security.service;
  2. import com.manong.config.security.handler.AnonymousAuthenticationHandler;
  3. import com.manong.config.security.handler.CustomerAccessDeniedHandler;
  4. import com.manong.config.security.handler.LoginFailureHandler;
  5. import com.manong.config.security.handler.LoginSuccessHandler;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  8. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  9. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  10. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  11. import org.springframework.security.config.http.SessionCreationPolicy;
  12. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  13. import org.springframework.stereotype.Component;
  14. import javax.annotation.Resource;
  15. @Component
  16. @EnableWebSecurity
  17. public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
  18. @Resource
  19. private LoginSuccessHandler loginSuccessHandler;
  20. @Resource
  21. private LoginFailureHandler loginFailureHandler;
  22. @Resource
  23. private CustomerAccessDeniedHandler customerAccessDeniedHandler;
  24. @Resource
  25. private AnonymousAuthenticationHandler anonymousAuthenticationHandler;
  26. @Resource
  27. private CustomerUserDetailService customerUserDetailService;
  28. //注入加密类
  29. @Bean
  30. public BCryptPasswordEncoder passwordEncoder(){
  31. return new BCryptPasswordEncoder();
  32. }
  33. //处理登录认证
  34. @Override
  35. protected void configure(HttpSecurity http) throws Exception {
  36. //登录过程处理
  37. http.formLogin() //表单登录
  38. .loginProcessingUrl("/api/user/login") //登录请求url地址
  39. .successHandler(loginSuccessHandler) //认证成功
  40. .failureHandler(loginFailureHandler) //认证失败
  41. .and()
  42. .csrf().disable()
  43. .sessionManagement()
  44. .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不创建Session
  45. .and().authorizeRequests() //设置需要拦截的请求
  46. .antMatchers("/api/user/login").permitAll()//登录放行
  47. .anyRequest().authenticated() //其他请求一律拦截
  48. .and()
  49. .exceptionHandling()
  50. .authenticationEntryPoint(anonymousAuthenticationHandler) //匿名无权限类
  51. .accessDeniedHandler(customerAccessDeniedHandler) //认证用户无权限
  52. .and()
  53. .cors();//支持跨域
  54. }
  55. //认证配置处理器
  56. @Override
  57. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  58. auth.userDetailsService(customerUserDetailService)
  59. .passwordEncoder(this.passwordEncoder());//密码加密
  60. }
  61. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/870197
推荐阅读
相关标签
  

闽ICP备14008679号