Spring Security实现多种登录方式

[复制链接]
发表于 2026-2-9 22:06:52 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1.Security 先容


  • 官网链接:https://docs.spring.io/spring-security/reference/5.7.6/servlet/architecture.html
依靠导入
  1.    <parent>
  2.         <groupId>org.springframework.boot</groupId>
  3.         <artifactId>spring-boot-starter-parent</artifactId>
  4.         <version>2.7.8</version>
  5.         <relativePath/> <!-- lookup parent from repository -->
  6.     </parent>
  7. <dependencys>
  8.      <dependency>
  9.             <groupId>org.springframework.boot</groupId>
  10.             <artifactId>spring-boot-starter-security</artifactId>
  11.         </dependency>
  12. </dependencys>
复制代码
2.Security 自界说拦截器实现多种登录方式

1. 账户暗码登录

  1. 1.自定义MyUsernamePasswordFilter
复制代码
  1. /**
  2. * 2023/2/26
  3. *
  4. * @author cyh
  5. * 10:30
  6. */
  7. @Slf4j
  8. public class MyUsernamePasswordFilter extends AbstractAuthenticationProcessingFilter {
  9.     public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
  10.     public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
  11.     // 自定拦截路由
  12.     private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/user/login", "POST");
  13.     private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
  14.     private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
  15.     private boolean postOnly = true;
  16.     private FormUser user;
  17.     public MyUsernamePasswordFilter() {
  18.         super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
  19.     }
  20.     public MyUsernamePasswordFilter(AuthenticationManager authenticationManager) {
  21.         super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
  22.     }
  23.     public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
  24.         if (this.postOnly && !request.getMethod().equals("POST")) {
  25.             throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  26.         } else {
  27.             log.info("url:" + request.getRequestURI());
  28.             // 获取登录表单
  29.             getUser(request);
  30.             String username = obtainUsername();
  31.             username = username != null ? username.trim() : "";
  32.             String password = obtainPassword();
  33.             password = password != null ? password : "";
  34.             MyUsernamePasswordToken authRequest = MyUsernamePasswordToken.unauthenticated(username, password);
  35.             this.setDetails(request, authRequest);
  36.             this.setContinueChainBeforeSuccessfulAuthentication(false);
  37.             return this.getAuthenticationManager().authenticate(authRequest);
  38.         }
  39.     }
  40.     @Nullable
  41.     protected String obtainPassword() {
  42.         return user.getPassword();
  43.     }
  44.     @Nullable
  45.     protected String obtainUsername() {
  46.         return user.getUsername();
  47.     }
  48.     protected void setDetails(HttpServletRequest request, MyUsernamePasswordToken authRequest) {
  49.         authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
  50.     }
  51.     public void setUsernameParameter(String usernameParameter) {
  52.         Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
  53.         this.usernameParameter = usernameParameter;
  54.     }
  55.     public void setPasswordParameter(String passwordParameter) {
  56.         Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
  57.         this.passwordParameter = passwordParameter;
  58.     }
  59.     public void setPostOnly(boolean postOnly) {
  60.         this.postOnly = postOnly;
  61.     }
  62.     public final String getUsernameParameter() {
  63.         return this.usernameParameter;
  64.     }
  65.     public final String getPasswordParameter() {
  66.         return this.passwordParameter;
  67.     }
  68.     /**
  69.      * 获取登录用户信息
  70.      *
  71.      * @param request
  72.      * @return
  73.      */
  74.     private void getUser(HttpServletRequest request) {
  75.         ObjectMapper objectMapper = new ObjectMapper();
  76.         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  77.         try {
  78.             user = objectMapper.readValue(request.getInputStream(), FormUser.class);
  79.             log.info("user:" + user.toString());
  80.         } catch (Exception e) {
  81.             throw new NullPointerException("获取不到登录信息");
  82.         }
  83.     }
  84. }
复制代码

  • 自界说provider
  1. @Slf4j
  2. public class MyUsernamePasswordProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
  3.     private final MyUserDetailsService userDetailsService;
  4.     private final PasswordEncoder passwordEncoder;
  5.     private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
  6.     public MyUsernamePasswordProvider(MyUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
  7.         this.userDetailsService = userDetailsService;
  8.         this.passwordEncoder = passwordEncoder;
  9.     }
  10.     @Override
  11.     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  12.         try {
  13.             MyUsernamePasswordToken tokenReq = (MyUsernamePasswordToken) authentication;
  14.             String username = tokenReq.getPrincipal().toString();
  15.             String password = tokenReq.getCredentials().toString();
  16.             LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(username);
  17.             if (!passwordEncoder.matches(password, loginUser.getPassword())) {
  18.                 throw new RuntimeException("账号或者密码错误");
  19.             }
  20.             // 将权限列表加入MyUsernamePasswordToken
  21.             MyUsernamePasswordToken myUsernameAuthenticationToken = MyUsernamePasswordToken.authenticated(loginUser, null, null);
  22.             myUsernameAuthenticationToken.setDetails(authentication.getDetails());
  23.             return myUsernameAuthenticationToken;
  24.         } catch (Exception e) {
  25.             e.printStackTrace();
  26.             throw new BadCredentialsException(e.getMessage());
  27.         }
  28.     }
  29.     @Override
  30.     public boolean supports(Class<?> authentication) {
  31.         return (MyUsernamePasswordToken.class.isAssignableFrom(authentication));
  32.     }
  33.     @Override
  34.     public void afterPropertiesSet() throws Exception {
  35.         Assert.notNull(userDetailsService, "userDetailsService must not be null");
  36.         Assert.notNull(userDetailsService, "userDetailsService must not be null");
  37.     }
  38.     @Override
  39.     public void setMessageSource(MessageSource messageSource) {
  40.         this.messages = new MessageSourceAccessor(messageSource);
  41.     }
复制代码

  • 自界说Token
  1. public class MyUsernamePasswordToken extends AbstractAuthenticationToken {
  2.     private static final long serialVersionUID = 570L;
  3.     private final Object principal;
  4.     private Object credentials;
  5.     public MyUsernamePasswordToken(Object principal, Object credentials) {
  6.         super((Collection) null);
  7.         this.principal = principal;
  8.         this.credentials = credentials;
  9.         this.setAuthenticated(false);
  10.     }
  11.     public MyUsernamePasswordToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
  12.         super(authorities);
  13.         this.principal = principal;
  14.         this.credentials = credentials;
  15.         super.setAuthenticated(true);
  16.     }
  17.     public static MyUsernamePasswordToken unauthenticated(Object principal, Object credentials) {
  18.         return new MyUsernamePasswordToken(principal, credentials);
  19.     }
  20.     public static MyUsernamePasswordToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
  21.         return new MyUsernamePasswordToken(principal, credentials, authorities);
  22.     }
  23.     public Object getCredentials() {
  24.         return this.credentials;
  25.     }
  26.     public Object getPrincipal() {
  27.         return this.principal;
  28.     }
  29.     public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
  30.         Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
  31.         super.setAuthenticated(false);
  32.     }
  33.     public void eraseCredentials() {
  34.         super.eraseCredentials();
  35.         this.credentials = null;
  36.     }
  37. }
复制代码

  • 自界说UserDetails
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. public class LoginUser implements UserDetails, Serializable {
  5.     private static final long serialVersionUID = -53711580203962796L;
  6.     private User user;https://www.yuque.com/u37030796/eb88se/uekda8twfz1pdq50#db5c42be
  7.     private List<Long> permissions;
  8.     public LoginUser(User user, List<Long> permissions) {
  9.         this.user = user;
  10.         this.permissions = permissions;
  11.     }
  12.     private List<SimpleGrantedAuthority> authorities;
  13.     @Override
  14.     public Collection<? extends GrantedAuthority> getAuthorities() {
  15.         if (authorities != null) {
  16.             return authorities;
  17.         }
  18.         if (CollUtil.isEmpty(permissions)) {
  19.             return null;
  20.         }
  21.         authorities = permissions.stream().filter(x -> !ObjectUtil.isNull(x)).map(x -> x.toString())
  22.                 .map(SimpleGrantedAuthority::new)
  23.                 .collect(Collectors.toList());
  24.         return authorities;
  25.     }
  26.     @Override
  27.     public String getPassword() {
  28.         return user.getPassword();
  29.     }
  30.     @Override
  31.     public String getUsername() {
  32.         return user.getUsername();
  33.     }
  34.     @Override
  35.     public boolean isAccountNonExpired() {
  36.         return true;
  37.     }
  38.     @Override
  39.     public boolean isAccountNonLocked() {
  40.         return true;
  41.     }
  42.     @Override
  43.     public boolean isCredentialsNonExpired() {
  44.         return true;
  45.     }
  46.     @Override
  47.     public boolean isEnabled() {
  48.         return true;
  49.     }
  50. }
复制代码

  • 自界说UserDetailsService
  1. @Slf4j
  2. @Service
  3. public class MyUserDetailsService implements UserDetailsService {
  4.     @Autowired
  5.     UserMapper userMapper;
  6.     @Override
  7.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  8.         log.info("userDetails");
  9.         LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
  10.         wrapper.eq(User::getUsername, username);
  11.         User user = userMapper.selectOne(wrapper);
  12.         if (Objects.isNull(user)) {
  13.             throw new UserNotFindException("找不到用户");
  14.         }
  15.         if (user.getStatus() == 1) {
  16.             throw new UserNotFindException("账户已经被禁用");
  17.         }
  18.         return new LoginUser(user, null);
  19.     }
  20. }
复制代码
  1. 6.自定义成功行为
复制代码
  1. /**
  2. * 2023/2/26
  3. *
  4. * @author cyh
  5. * 15:04
  6. */
  7. @Slf4j
  8. @Component
  9. public class MyUsernamePasswordSuccessHandler implements AuthenticationSuccessHandler {
  10.     @Autowired
  11.     RedisTemplate<String, String> redisTemplate;
  12.     @Autowired
  13.     UserRoleService userRoleService;
  14.     @Autowired
  15.     IUserService userService;
  16.     @Override
  17.     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
  18.         chain.doFilter(request, response);
  19.     }
  20.     @Override
  21.     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
  22.         response.setCharacterEncoding("utf-8");
  23.         response.setContentType("application/json;charset=utf-8");
  24.         LoginUser loginUser = (LoginUser) authentication.getPrincipal();
  25.         Long uid = loginUser.getUser().getId();
  26.         // 每次登录添加登录ip和时间
  27.         //  获取权限列表并返回
  28.         User user = loginUser.getUser();
  29.         user.setLastLoginTime(LocalDateTime.now().toString());
  30.         user.setLoginIp(ServletUtil.getClientIP(request));
  31.         userService.updateById(user);
  32.         String token = MyJwtUtil.getToken(uid, RequestKey.ADMIN_USER.getKey());
  33.         redisTemplate.opsForValue().set(RequestKey.ADMIN_USER.getKey() + "login:" + uid, token, 60 * 60 * 72, TimeUnit.SECONDS);
  34.         // TODO redis存放门店列表
  35.         // TODO redis存放权限列表
  36.         List<Long> permsByUserId = userRoleService.getPermsByUserId(uid);
  37.         ResultAuthority resultAuthority = new ResultAuthority(token, permsByUserId);
  38.         ResultData<ResultAuthority> ok = ResultData.success("ok", resultAuthority);
  39.         response.getWriter().print(JSONUtil.parseObj(ok));
  40.         log.info("登录成功:" + authentication.getPrincipal().toString());
  41.     }
  42. }
复制代码
  1. 7.自定义错误返回
复制代码
  1. @Slf4j
  2. public class MyUsernamePasswordErrorHandler implements AuthenticationFailureHandler {
  3.     @Override
  4.     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  5.         log.info(exception.getMessage());
  6.         response.setCharacterEncoding("utf-8");
  7.         response.setContentType("application/json;charset=utf-8");
  8.         response.setStatus(200);
  9.         ResultData<ResultAuthority> ok = ResultData.fail(500, exception.getMessage());
  10.         response.getWriter().print(JSONUtil.parseObj(ok));
  11.     }
  12. }
复制代码
3. 微信授权登录


  • 微信登录拦截器
  1. /**
  2. * 2023/3/6
  3. *
  4. * @author cyh
  5. * 9:29
  6. */
  7. @Slf4j
  8. public class WxLoginFilter extends AbstractAuthenticationProcessingFilter {
  9.     private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/wxlogin/userinfo", "GET");
  10.     private boolean postOnly = true;
  11.     public WxLoginFilter() {
  12.         super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
  13.     }
  14.     public WxLoginFilter(AuthenticationManager authenticationManager) {
  15.         super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
  16.     }
  17.     @Override
  18.     public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
  19.         log.info("正在进行拦截");
  20.         if (!request.getMethod().equals("GET")) {
  21.             throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
  22.         } else {
  23.             // 从请求头里获取到code
  24.             String code = obtainCode(request);
  25.             code = code != null ? code.trim() : "";
  26.             WxUserToken wxUserToken = WxUserToken.unauthenticated(code, null);
  27.             this.setDetails(request, wxUserToken);
  28.             return this.getAuthenticationManager().authenticate(wxUserToken);
  29.         }
  30.     }
  31.     protected void setDetails(HttpServletRequest request, WxUserToken authRequest) {
  32.         authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
  33.     }
  34.     @Nullable
  35.     protected String obtainCode(HttpServletRequest request) {
  36.         return request.getParameter("code");
  37.     }
  38.     public void setPostOnly(boolean postOnly) {
  39.         this.postOnly = postOnly;
  40.     }
  41. }
复制代码

  • 微信登录provider
  1. /**
  2. * 2023/3/6
  3. *
  4. * @author cyh
  5. * 9:35
  6. */
  7. @Slf4j
  8. public class WxLoginProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
  9.     private final WxLoginServiceImpl wxLoginService;
  10.     private final WxLoginUtilService wxLoginUtilService;
  11.     private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
  12.     public WxLoginProvider(WxLoginServiceImpl userDetailsService, WxLoginUtilService wxLoginUtilService) {
  13.         this.wxLoginService = userDetailsService;
  14.         this.wxLoginUtilService = wxLoginUtilService;
  15.     }
  16.     @Override
  17.     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  18.         WxUserToken tokenReq = (WxUserToken) authentication;
  19.         String code = (String) tokenReq.getPrincipal();
  20.         WxUserToken wxUserToken = null;
  21.         String openid = null;
  22.         // 根据code拿到access_token和openid
  23.         try {
  24.             // 向微信获取
  25.             WxOAuth2AccessToken accessToken = wxLoginUtilService.getAccessTokenAndOpenID(code);
  26.             openid = wxLoginUtilService.getWxUserInfo(accessToken);
  27.         } catch (WxErrorException e) {
  28.             throw new RuntimeException(e);
  29.         }
  30.         // 从数据库里获取用户信息并设置认证通过
  31.         WxLoginEntity wxLoginEntity = wxLoginService.loadUserByUsername(openid);
  32.         log.info("userDetails:{}", wxLoginEntity.toString());
  33.         wxUserToken = WxUserToken.authenticated(wxLoginEntity, null, null);
  34.         wxUserToken.setDetails(authentication.getDetails());
  35.         return wxUserToken;
  36.     }
  37.     @Override
  38.     public boolean supports(Class<?> authentication) {
  39.         return (WxUserToken.class.isAssignableFrom(authentication));
  40.     }
  41.     @Override
  42.     public void afterPropertiesSet() throws Exception {
  43.         Assert.notNull(wxLoginService, "userDetailsService must not be null");
  44.     }
  45.     @Override
  46.     public void setMessageSource(MessageSource messageSource) {
  47.         this.messages = new MessageSourceAccessor(messageSource);
  48.     }
  49. }
复制代码

  • 微信登录的Token
  1. /**
  2. * 2023/3/6
  3. *
  4. * @author cyh
  5. * 9:33
  6. */
  7. public class WxUserToken extends AbstractAuthenticationToken {
  8.     private static final long serialVersionUID = 570L;
  9.     private final Object principal;
  10.     private Object credentials;
  11.     public WxUserToken(Object principal, Object credentials) {
  12.         super((Collection) null);
  13.         this.principal = principal;
  14.         this.credentials = credentials;
  15.         this.setAuthenticated(false);
  16.     }
  17.     public WxUserToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
  18.         super(authorities);
  19.         this.principal = principal;
  20.         this.credentials = credentials;
  21.         super.setAuthenticated(true);
  22.     }
  23.     public static WxUserToken unauthenticated(Object principal, Object credentials) {
  24.         return new WxUserToken(principal, credentials);
  25.     }
  26.     public static WxUserToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
  27.         return new WxUserToken(principal, credentials, authorities);
  28.     }
  29.     public Object getCredentials() {
  30.         return this.credentials;
  31.     }
  32.     public Object getPrincipal() {
  33.         return this.principal;
  34.     }
  35.     public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
  36.         Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
  37.         super.setAuthenticated(false);
  38.     }
  39.     public void eraseCredentials() {
  40.         super.eraseCredentials();
  41.         this.credentials = null;
  42.     }
  43. }
复制代码

  • 微信登录的UserDetails
  1. /**
  2. * 2023/3/6
  3. *
  4. * @author cyh
  5. * 14:12
  6. */
  7. @Data
  8. @AllArgsConstructor
  9. public class WxLoginEntity implements UserDetails {
  10.     private OrderUser user;
  11.     @Override
  12.     public Collection<? extends GrantedAuthority> getAuthorities() {
  13.         return null;
  14.     }
  15.     @Override
  16.     public String getPassword() {
  17.         return null;
  18.     }
  19.     @Override
  20.     public String getUsername() {
  21.         return user.getOpenid();
  22.     }
  23.     @Override
  24.     public boolean isAccountNonExpired() {
  25.         return true;
  26.     }
  27.     @Override
  28.     public boolean isAccountNonLocked() {
  29.         return true;
  30.     }
  31.     @Override
  32.     public boolean isCredentialsNonExpired() {
  33.         return true;
  34.     }
  35.     @Override
  36.     public boolean isEnabled() {
  37.         return true;
  38.     }
  39. }
复制代码

  • 微信登录的UserDetailsService
  1. @Slf4j
  2. @Service
  3. public class WxLoginServiceImpl implements UserDetailsService {
  4.     @Autowired
  5.     WxLoginUtilService wxLoginUtilService;
  6.     @Autowired
  7.     IOrderUserService orderUserService;
  8.     @Value("${wxlogin.appid}")
  9.     private String appid;
  10.     /**
  11.      * 获取数据库用户信息
  12.      *
  13.      * @param username
  14.      * @return
  15.      * @throws UsernameNotFoundException
  16.      */
  17.     @Override
  18.     public WxLoginEntity loadUserByUsername(String username) throws UsernameNotFoundException {
  19.         OrderUser orderUser;
  20.         orderUser = orderUserService.getOne(new LambdaQueryWrapper<OrderUser>().eq(OrderUser::getOpenid, username));
  21.         if (ObjectUtil.isNull(orderUser)) {
  22.             throw new UsernameNotFoundException("找不到用户");
  23.         }
  24.         return new WxLoginEntity(orderUser);
  25.     }
  26. }
复制代码

  • 乐成返回
  1. @Slf4j
  2. @Component
  3. public class WxLoginSuccessHandler implements AuthenticationSuccessHandler {
  4.     @Autowired
  5.     RedisTemplate<String, String> redisTemplate;
  6.     private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
  7.     @Override
  8.     @IgnoreResponseAdvice
  9.     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
  10.         // 获取当前认证的用户
  11.         WxLoginEntity loginUser = (WxLoginEntity) authentication.getPrincipal();
  12.         // 获取用户的id
  13.         Long uid = loginUser.getUser().getId();
  14.         // 获取token
  15.         String token = MyJwtUtil.getToken(uid, RequestKey.WX_USER.getKey());
  16.         redisTemplate.opsForValue().set(RequestKey.WX_USER.getKey() + "login:" + loginUser.getUser().getId(), token, 60 * 60 * 72, TimeUnit.SECONDS);
  17.         // 设置返回的id和token
  18.         response.setCharacterEncoding("utf-8");
  19.         response.setContentType("application/json;charset=utf-8");
  20.         Map<String, String> map = new HashMap<>();
  21.         map.put("nickname", loginUser.getUser().getNickname());
  22.         map.put("openid", loginUser.getUser().getOpenid());
  23.         map.put("token", token);
  24.         // 返回给前端
  25.         ResultData<Map> ok = ResultData.success("ok", map);
  26.         response.getWriter().print(JSONUtil.parseObj(ok));
  27.     }
  28. }
复制代码

  • 失败返回
  1. /**
  2. * 2023/3/6
  3. *
  4. * @author cyh
  5. * 9:35
  6. */
  7. public class WxLoginErrorHandler implements AuthenticationFailureHandler {
  8.     @Override
  9.     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
  10.         throw new RuntimeException("授权登录验证失败");
  11.     }
  12. }
复制代码
自界说token拦截器

  1. @Slf4j
  2. public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  3.     /**
  4.      * 请求头token的key
  5.      */
  6.     private String tokenKey = RequestKey.TOKEN_KEY.getKey();
  7.     /**
  8.      * 用户的value
  9.      */
  10.     private String wxUser = RequestKey.WX_USER.getKey();
  11.     /**
  12.      * 管理用户的value
  13.      */
  14.     private String adminUser = RequestKey.ADMIN_USER.getKey();
  15.     /**
  16.      * redis 工具
  17.      */
  18.     private RedisTemplate<String, String> redisTemplate;
  19.     /**
  20.      * 微信登录用户
  21.      */
  22.     private IOrderUserService orderUserService;
  23.     /**
  24.      * 管理用户
  25.      */
  26.     private IUserService userService;
  27.     /**
  28.      * 用户权限
  29.      */
  30.     private UserRoleService userRoleService;
  31.     public JwtAuthenticationTokenFilter(IOrderUserService orderUserService, IUserService userService, UserRoleService userRoleService, RedisTemplate<String, String> redisTemplate) {
  32.         this.orderUserService = orderUserService;
  33.         this.userService = userService;
  34.         this.userRoleService = userRoleService;
  35.         this.redisTemplate = redisTemplate;
  36.     }
  37.     @Override
  38.     protected void doFilterInternal(HttpServletRequest request,
  39.                                     HttpServletResponse response,
  40.                                     FilterChain chain) throws ServletException, IOException {
  41.         String reqToken = request.getHeader(this.tokenKey);
  42.         log.info("现在{}正在进行jwt认证:{}", request.getRequestURI(), reqToken);
  43.         // 判断token和请求用户是否存在
  44.         if (Strings.isBlank(reqToken)) {
  45.             chain.doFilter(request, response);
  46.             return;
  47.         }
  48.         try {
  49.             // 判断token是否过期
  50.             if (!MyJwtUtil.verifyToken(reqToken)) {
  51.                 throw new TokenException("非法token, token已过期");
  52.             }
  53.             // 从token上获取uid
  54.             Long uid = MyJwtUtil.getUid(reqToken);
  55.             String reqUser = MyJwtUtil.getRoleUser(reqToken);
  56.             if (ObjectUtil.isNull(uid) || ObjectUtil.isNull(reqUser)) {
  57.                 throw new TokenException("非法token.uid获取不到");
  58.             }
  59.             if (StrUtil.equals(reqUser, wxUser)) {
  60.                 // 与redis中的用户token对比
  61.                 String redisToken = redisTemplate.opsForValue().get(wxUser + "login:" + uid);
  62.                 log.info("redisToken:{}", redisToken);
  63.                 if (StrUtil.isBlankIfStr(redisToken) || !StrUtil.equals(redisToken, reqToken)) {
  64.                     throw new TokenException("非法token,与redis中不一致");
  65.                 }
  66.                 // 判断获取的用户存不存在
  67.                 OrderUser user = orderUserService.getById(uid);
  68.                 if (ObjectUtil.isNull(user)) {
  69.                     throw new TokenException("非法token,登录用户类别获取不到");
  70.                 }
  71.                 // 存在设置认证通过
  72.                 WxLoginEntity wxLoginEntity = new WxLoginEntity(user);
  73.                 WxUserToken wxUserToken = WxUserToken.authenticated(wxLoginEntity, null, null);
  74.                 SecurityContextHolder.getContext().setAuthentication(wxUserToken);
  75.                 chain.doFilter(request, response);
  76.                 return;
  77.             }
  78.             if (StrUtil.equals(reqUser, adminUser)) {
  79.                 // 与redis中的用户token对比
  80.                 String redisToken = redisTemplate.opsForValue().get(adminUser + "login:" + uid);
  81.                 log.info("redisToken:{}", redisToken);
  82.                 if (StrUtil.isBlankIfStr(redisToken) || !StrUtil.equals(redisToken, reqToken)) {
  83.                     throw new TokenException("非法token,与redis中不一致");
  84.                 }
  85.                 // TODO 从redis获取用户信息
  86.                 User byId = userService.getById(uid);
  87.                 // TODO 将用户信息放入MyUsernamePasswordToken
  88.                 List<Long> permsByUserId = userRoleService.getPermsByUserId(uid);
  89.                 LoginUser loginUser = new LoginUser(byId, permsByUserId);
  90.                 if (loginUser.getUser().getStatus() == 1) {
  91.                     redisTemplate.delete(adminUser + "login:" + uid);
  92.                     throw new TokenException("账户被禁用,请重新登录");
  93.                 }
  94.                 MyUsernamePasswordToken myUsernamePasswordToken = new MyUsernamePasswordToken(loginUser, null,
  95.                         loginUser.getAuthorities());
  96.                 // TODO 将认证的信息放入 SecurityContextHolder.getContext()
  97.                 SecurityContextHolder.getContext().setAuthentication(myUsernamePasswordToken);
  98.                 chain.doFilter(request, response);
  99.                 return;
  100.             }
  101.             throw new TokenException("非法token");
  102.         } catch (TokenException exception) {
  103.             JwtUnAuthorizedResponseAuthenticationEntryPoint point = new JwtUnAuthorizedResponseAuthenticationEntryPoint();
  104.             point.commence(request, response, exception);
  105.         }
  106.     }
  107. }
复制代码
4. Security设置类

  1. @Configuration
  2. @EnableWebSecurity    // 添加 security 过滤器\
  3. @EnableGlobalMethodSecurity(prePostEnabled = true)
  4. public class SecurityConfig {
  5.     /**
  6.      * token认证器
  7.      */
  8.     @Autowired
  9.     IOrderUserService orderUserService;
  10.     @Autowired
  11.     IUserService userService;
  12.     @Autowired
  13.     MyJwtUtil myJwtUtil;
  14.     @Autowired
  15.     UserRoleService userRoleService;
  16.     @Autowired
  17.     RedisTemplate<String, String> redisTemplate;
  18.     JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
  19.         return new JwtAuthenticationTokenFilter(orderUserService, userService, myJwtUtil, userRoleService, redisTemplate);
  20.     }
  21.     /**
  22.      * 放行路径配置类
  23.      */
  24.     @Autowired
  25.     SecurityRelease securityRelease;
  26.     /**
  27.      * 放行路径
  28.      */
  29.     @Bean
  30.     public PasswordEncoder passwordEncoder() {
  31.         return new BCryptPasswordEncoder();
  32.     }
  33.     /**
  34.      * 获取AuthenticationManager(认证管理器),登录时认证使用
  35.      *
  36.      * @return
  37.      * @throws Exception
  38.      */
  39.     @Bean
  40.     public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
  41.         AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
  42.         return authenticationManager;
  43.     }
  44.     @Bean
  45.     SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
  46.         http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl());
  47.         return
  48.                 http.authorizeRequests(authorize -> authorize
  49.                                 .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
  50.                                 // 请求放开
  51.                                 .antMatchers(securityRelease.getReleaseUrl()).permitAll()
  52.                                 // 其他地址的访问均需验证权限
  53.                                 .anyRequest().authenticated()
  54.                         ) // 基于 token,不需要 csrf
  55.                         .csrf().disable()
  56.                         .formLogin().disable()
  57.                         // 基于 token,不需要 session
  58.                         .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
  59.                         .addFilterAt(wxLoginFilter(), UsernamePasswordAuthenticationFilter.class)
  60.                         .addFilterAt(usernamePasswordFilter(), UsernamePasswordAuthenticationFilter.class)
  61.                         .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
  62.                         .build();
  63.     }
  64.     /**
  65.      * 配置跨源访问(CORS)
  66.      *
  67.      * @return
  68.      */
  69.     @Bean
  70.     CorsConfigurationSource corsConfigurationSource() {
  71.         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
  72.         source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
  73.         return source;
  74.     }
  75.     @Autowired
  76.     WxLoginServiceImpl wxLoginService;
  77.     @Autowired
  78.     WxLoginUtilService wxLoginUtilService;
  79.     @Autowired
  80.     WxLoginSuccessHandler wxLoginSuccessHandler;
  81.     // 添加微信用户登录拦截器
  82.     WxLoginFilter wxLoginFilter() throws Exception {
  83.         WxLoginFilter myUsernameFilter = new WxLoginFilter();
  84.         ProviderManager providerManager = new ProviderManager(Collections.singletonList(wxLoginProvider()));
  85.         myUsernameFilter.setAuthenticationManager(providerManager);
  86.         myUsernameFilter.setAuthenticationSuccessHandler(wxLoginSuccessHandler);
  87.         myUsernameFilter.setAuthenticationFailureHandler(new WxLoginErrorHandler());
  88.         myUsernameFilter.setContinueChainBeforeSuccessfulAuthentication(true);
  89.         return myUsernameFilter;
  90.     }
  91.     WxLoginProvider wxLoginProvider() {
  92.         WxLoginProvider provider = new WxLoginProvider(wxLoginService, wxLoginUtilService);
  93.         return provider;
  94.     }
  95.     @Autowired
  96.     MyUsernamePasswordSuccessHandler myUsernamePasswordSuccessHandler;
  97.     @Autowired
  98.     MyUserDetailsService userDetailsService;
  99.     /**
  100.      * 添加用户登录拦截器
  101.      *
  102.      * @return
  103.      */
  104.     MyUsernamePasswordFilter usernamePasswordFilter() {
  105.         MyUsernamePasswordFilter filter = new MyUsernamePasswordFilter();
  106.         ProviderManager manager = new ProviderManager(Collections.singletonList(usernamePasswordProvider()));
  107.         filter.setAuthenticationManager(manager);
  108.         filter.setAuthenticationFailureHandler(new MyUsernamePasswordErrorHandler());
  109.         filter.setAuthenticationSuccessHandler(myUsernamePasswordSuccessHandler);
  110.         return filter;
  111.     }
  112.     MyUsernamePasswordProvider usernamePasswordProvider() {
  113.         return new MyUsernamePasswordProvider(userDetailsService, passwordEncoder());
  114.     }
  115. }
复制代码
4.yml 文件设置放行路径

  1. my-security:
  2.   releaseUrl:
  3.     - /wxlogin/**
  4.     - /user/login
  5.     - /user/logout
  6.     - /doc.html
  7.     - /webjars/**
  8.     - /swagger-resources/**
  9.     - /v2/**
  10.     - /wx-pay/**
  11.     - /**
复制代码
5. 放行路径设置类

  1. /**
  2. * 2023/3/13
  3. * security 放行路径配置类
  4. *
  5. * @author cyh
  6. * 14:27
  7. */
  8. @Configuration
  9. @Data
  10. @ConfigurationProperties(prefix = "my-security")
  11. public class SecurityRelease {
  12.     private String[] releaseUrl;
  13. }
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表