Spring Security实现多种登录方式

1.Security 介绍

  1. 官网链接:https://docs.spring.io/spring-security/reference/5.7.6/servlet/architecture.html

依赖导入

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.8</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
<dependencys>
     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
</dependencys>

2.Security 自定义拦截器实现多种登录方式

1. 账户密码登录

1.自定义MyUsernamePasswordFilter
/**
 * 2023/2/26
 *
 * @author cyh
 * 10:30
 */
@Slf4j
public class MyUsernamePasswordFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    // 自定拦截路由
    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/user/login", "POST");
    private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
    private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
    private boolean postOnly = true;
    private FormUser user;


    public MyUsernamePasswordFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    public MyUsernamePasswordFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            log.info("url:" + request.getRequestURI());
            // 获取登录表单
            getUser(request);
            String username = obtainUsername();
            username = username != null ? username.trim() : "";
            String password = obtainPassword();
            password = password != null ? password : "";
            MyUsernamePasswordToken authRequest = MyUsernamePasswordToken.unauthenticated(username, password);
            this.setDetails(request, authRequest);
            this.setContinueChainBeforeSuccessfulAuthentication(false);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    @Nullable
    protected String obtainPassword() {
        return user.getPassword();
    }

    @Nullable
    protected String obtainUsername() {
        return user.getUsername();
    }


    protected void setDetails(HttpServletRequest request, MyUsernamePasswordToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setUsernameParameter(String usernameParameter) {
        Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
        this.usernameParameter = usernameParameter;
    }

    public void setPasswordParameter(String passwordParameter) {
        Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
        this.passwordParameter = passwordParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getUsernameParameter() {
        return this.usernameParameter;
    }

    public final String getPasswordParameter() {
        return this.passwordParameter;
    }

    /**
     * 获取登录用户信息
     *
     * @param request
     * @return
     */
    private void getUser(HttpServletRequest request) {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        try {
            user = objectMapper.readValue(request.getInputStream(), FormUser.class);
            log.info("user:" + user.toString());
        } catch (Exception e) {
            throw new NullPointerException("获取不到登录信息");
        }
    }

}
  1. 自定义provider
@Slf4j
public class MyUsernamePasswordProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {


    private final MyUserDetailsService userDetailsService;
    private final PasswordEncoder passwordEncoder;
    private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

    public MyUsernamePasswordProvider(MyUserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        try {
            MyUsernamePasswordToken tokenReq = (MyUsernamePasswordToken) authentication;
            String username = tokenReq.getPrincipal().toString();
            String password = tokenReq.getCredentials().toString();
            LoginUser loginUser = (LoginUser) userDetailsService.loadUserByUsername(username);
            if (!passwordEncoder.matches(password, loginUser.getPassword())) {
                throw new RuntimeException("账号或者密码错误");
            }
            // 将权限列表加入MyUsernamePasswordToken
            MyUsernamePasswordToken myUsernameAuthenticationToken = MyUsernamePasswordToken.authenticated(loginUser, null, null);
            myUsernameAuthenticationToken.setDetails(authentication.getDetails());
            return myUsernameAuthenticationToken;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BadCredentialsException(e.getMessage());
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (MyUsernamePasswordToken.class.isAssignableFrom(authentication));
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(userDetailsService, "userDetailsService must not be null");
        Assert.notNull(userDetailsService, "userDetailsService must not be null");
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }
  1. 自定义Token
public class MyUsernamePasswordToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 570L;
    private final Object principal;
    private Object credentials;

    public MyUsernamePasswordToken(Object principal, Object credentials) {
        super((Collection) null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    public MyUsernamePasswordToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

    public static MyUsernamePasswordToken unauthenticated(Object principal, Object credentials) {
        return new MyUsernamePasswordToken(principal, credentials);
    }

    public static MyUsernamePasswordToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        return new MyUsernamePasswordToken(principal, credentials, authorities);
    }

    public Object getCredentials() {
        return this.credentials;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        super.setAuthenticated(false);
    }

    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }
}
  1. 自定义UserDetails
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUser implements UserDetails, Serializable {
    private static final long serialVersionUID = -53711580203962796L;

    private User user;https://www.yuque.com/u37030796/eb88se/uekda8twfz1pdq50#db5c42be
    private List<Long> permissions;

    public LoginUser(User user, List<Long> permissions) {
        this.user = user;
        this.permissions = permissions;
    }

    private List<SimpleGrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if (authorities != null) {
            return authorities;
        }
        if (CollUtil.isEmpty(permissions)) {
            return null;
        }
        authorities = permissions.stream().filter(x -> !ObjectUtil.isNull(x)).map(x -> x.toString())
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  1. 自定义UserDetailsService
@Slf4j
@Service
public class MyUserDetailsService implements UserDetailsService {


    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("userDetails");
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(wrapper);
        if (Objects.isNull(user)) {
            throw new UserNotFindException("找不到用户");
        }
        if (user.getStatus() == 1) {
            throw new UserNotFindException("账户已经被禁用");
        }
        return new LoginUser(user, null);
    }

}
6.自定义成功行为
/**
 * 2023/2/26
 *
 * @author cyh
 * 15:04
 */
@Slf4j
@Component
public class MyUsernamePasswordSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    RedisTemplate<String, String> redisTemplate;
    @Autowired
    UserRoleService userRoleService;
    @Autowired
    IUserService userService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        chain.doFilter(request, response);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Long uid = loginUser.getUser().getId();

        // 每次登录添加登录ip和时间
        //  获取权限列表并返回
        User user = loginUser.getUser();
        user.setLastLoginTime(LocalDateTime.now().toString());
        user.setLoginIp(ServletUtil.getClientIP(request));
        userService.updateById(user);
        String token = MyJwtUtil.getToken(uid, RequestKey.ADMIN_USER.getKey());
        redisTemplate.opsForValue().set(RequestKey.ADMIN_USER.getKey() + "login:" + uid, token, 60 * 60 * 72, TimeUnit.SECONDS);
        // TODO redis存放门店列表
        // TODO redis存放权限列表
        List<Long> permsByUserId = userRoleService.getPermsByUserId(uid);
        ResultAuthority resultAuthority = new ResultAuthority(token, permsByUserId);
        ResultData<ResultAuthority> ok = ResultData.success("ok", resultAuthority);
        response.getWriter().print(JSONUtil.parseObj(ok));
        log.info("登录成功:" + authentication.getPrincipal().toString());
    }
}
7.自定义错误返回
@Slf4j
public class MyUsernamePasswordErrorHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info(exception.getMessage());
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(200);
        ResultData<ResultAuthority> ok = ResultData.fail(500, exception.getMessage());
        response.getWriter().print(JSONUtil.parseObj(ok));
    }
}

3. 微信授权登录

  1. 微信登录拦截器

/**
 * 2023/3/6
 *
 * @author cyh
 * 9:29
 */
@Slf4j
public class WxLoginFilter extends AbstractAuthenticationProcessingFilter {

    private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/wxlogin/userinfo", "GET");
    private boolean postOnly = true;

    public WxLoginFilter() {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
    }

    public WxLoginFilter(AuthenticationManager authenticationManager) {
        super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        log.info("正在进行拦截");
        if (!request.getMethod().equals("GET")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            // 从请求头里获取到code
            String code = obtainCode(request);
            code = code != null ? code.trim() : "";
            WxUserToken wxUserToken = WxUserToken.unauthenticated(code, null);
            this.setDetails(request, wxUserToken);
            return this.getAuthenticationManager().authenticate(wxUserToken);
        }
    }

    protected void setDetails(HttpServletRequest request, WxUserToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    @Nullable
    protected String obtainCode(HttpServletRequest request) {
        return request.getParameter("code");
    }


    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }
}
  1. 微信登录provider
/**
 * 2023/3/6
 *
 * @author cyh
 * 9:35
 */
@Slf4j
public class WxLoginProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {

    private final WxLoginServiceImpl wxLoginService;
    private final WxLoginUtilService wxLoginUtilService;
    private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

    public WxLoginProvider(WxLoginServiceImpl userDetailsService, WxLoginUtilService wxLoginUtilService) {
        this.wxLoginService = userDetailsService;
        this.wxLoginUtilService = wxLoginUtilService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        WxUserToken tokenReq = (WxUserToken) authentication;
        String code = (String) tokenReq.getPrincipal();
        WxUserToken wxUserToken = null;
        String openid = null;
        // 根据code拿到access_token和openid
        try {
            // 向微信获取
            WxOAuth2AccessToken accessToken = wxLoginUtilService.getAccessTokenAndOpenID(code);
            openid = wxLoginUtilService.getWxUserInfo(accessToken);
        } catch (WxErrorException e) {
            throw new RuntimeException(e);
        }
        // 从数据库里获取用户信息并设置认证通过
        WxLoginEntity wxLoginEntity = wxLoginService.loadUserByUsername(openid);
        log.info("userDetails:{}", wxLoginEntity.toString());
        wxUserToken = WxUserToken.authenticated(wxLoginEntity, null, null);
        wxUserToken.setDetails(authentication.getDetails());
        return wxUserToken;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (WxUserToken.class.isAssignableFrom(authentication));
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(wxLoginService, "userDetailsService must not be null");
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }
}
  1. 微信登录的Token
/**
 * 2023/3/6
 *
 * @author cyh
 * 9:33
 */
public class WxUserToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 570L;
    private final Object principal;
    private Object credentials;

    public WxUserToken(Object principal, Object credentials) {
        super((Collection) null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    public WxUserToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }

    public static WxUserToken unauthenticated(Object principal, Object credentials) {
        return new WxUserToken(principal, credentials);
    }

    public static WxUserToken authenticated(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        return new WxUserToken(principal, credentials, authorities);
    }

    public Object getCredentials() {
        return this.credentials;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        super.setAuthenticated(false);
    }

    public void eraseCredentials() {
        super.eraseCredentials();
        this.credentials = null;
    }

}
  1. 微信登录的UserDetails

/**
 * 2023/3/6
 *
 * @author cyh
 * 14:12
 */
@Data
@AllArgsConstructor
public class WxLoginEntity implements UserDetails {

    private OrderUser user;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public String getPassword() {
        return null;
    }

    @Override
    public String getUsername() {
        return user.getOpenid();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  1. 微信登录的UserDetailsService
@Slf4j
@Service
public class WxLoginServiceImpl implements UserDetailsService {

    @Autowired
    WxLoginUtilService wxLoginUtilService;
    @Autowired
    IOrderUserService orderUserService;

    @Value("${wxlogin.appid}")
    private String appid;


    /**
     * 获取数据库用户信息
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public WxLoginEntity loadUserByUsername(String username) throws UsernameNotFoundException {
        OrderUser orderUser;
        orderUser = orderUserService.getOne(new LambdaQueryWrapper<OrderUser>().eq(OrderUser::getOpenid, username));
        if (ObjectUtil.isNull(orderUser)) {
            throw new UsernameNotFoundException("找不到用户");
        }
        return new WxLoginEntity(orderUser);
    }

}

  1. 成功返回
@Slf4j
@Component
public class WxLoginSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    RedisTemplate<String, String> redisTemplate;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    @IgnoreResponseAdvice
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        // 获取当前认证的用户
        WxLoginEntity loginUser = (WxLoginEntity) authentication.getPrincipal();
        // 获取用户的id
        Long uid = loginUser.getUser().getId();
        // 获取token
        String token = MyJwtUtil.getToken(uid, RequestKey.WX_USER.getKey());
        redisTemplate.opsForValue().set(RequestKey.WX_USER.getKey() + "login:" + loginUser.getUser().getId(), token, 60 * 60 * 72, TimeUnit.SECONDS);
        // 设置返回的id和token
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json;charset=utf-8");
        Map<String, String> map = new HashMap<>();
        map.put("nickname", loginUser.getUser().getNickname());
        map.put("openid", loginUser.getUser().getOpenid());
        map.put("token", token);
        // 返回给前端
        ResultData<Map> ok = ResultData.success("ok", map);
        response.getWriter().print(JSONUtil.parseObj(ok));
    }
}
  1. 失败返回
/**
 * 2023/3/6
 *
 * @author cyh
 * 9:35
 */
public class WxLoginErrorHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        throw new RuntimeException("授权登录验证失败");
    }
}

自定义token拦截器

@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * 请求头token的key
     */
    private String tokenKey = RequestKey.TOKEN_KEY.getKey();

    /**
     * 用户的value
     */
    private String wxUser = RequestKey.WX_USER.getKey();
    /**
     * 管理用户的value
     */
    private String adminUser = RequestKey.ADMIN_USER.getKey();
    /**
     * redis 工具
     */
    private RedisTemplate<String, String> redisTemplate;
    /**
     * 微信登录用户
     */
    private IOrderUserService orderUserService;
    /**
     * 管理用户
     */
    private IUserService userService;
    /**
     * 用户权限
     */
    private UserRoleService userRoleService;


    public JwtAuthenticationTokenFilter(IOrderUserService orderUserService, IUserService userService, UserRoleService userRoleService, RedisTemplate<String, String> redisTemplate) {
        this.orderUserService = orderUserService;
        this.userService = userService;
        this.userRoleService = userRoleService;
        this.redisTemplate = redisTemplate;
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String reqToken = request.getHeader(this.tokenKey);
        log.info("现在{}正在进行jwt认证:{}", request.getRequestURI(), reqToken);
        // 判断token和请求用户是否存在
        if (Strings.isBlank(reqToken)) {
            chain.doFilter(request, response);
            return;
        }
        try {
            // 判断token是否过期
            if (!MyJwtUtil.verifyToken(reqToken)) {
                throw new TokenException("非法token, token已过期");
            }
            // 从token上获取uid
            Long uid = MyJwtUtil.getUid(reqToken);
            String reqUser = MyJwtUtil.getRoleUser(reqToken);
            if (ObjectUtil.isNull(uid) || ObjectUtil.isNull(reqUser)) {
                throw new TokenException("非法token.uid获取不到");
            }
            if (StrUtil.equals(reqUser, wxUser)) {
                // 与redis中的用户token对比
                String redisToken = redisTemplate.opsForValue().get(wxUser + "login:" + uid);
                log.info("redisToken:{}", redisToken);
                if (StrUtil.isBlankIfStr(redisToken) || !StrUtil.equals(redisToken, reqToken)) {
                    throw new TokenException("非法token,与redis中不一致");
                }
                // 判断获取的用户存不存在
                OrderUser user = orderUserService.getById(uid);
                if (ObjectUtil.isNull(user)) {
                    throw new TokenException("非法token,登录用户类别获取不到");
                }
                // 存在设置认证通过
                WxLoginEntity wxLoginEntity = new WxLoginEntity(user);
                WxUserToken wxUserToken = WxUserToken.authenticated(wxLoginEntity, null, null);
                SecurityContextHolder.getContext().setAuthentication(wxUserToken);
                chain.doFilter(request, response);
                return;
            }
            if (StrUtil.equals(reqUser, adminUser)) {
                // 与redis中的用户token对比
                String redisToken = redisTemplate.opsForValue().get(adminUser + "login:" + uid);
                log.info("redisToken:{}", redisToken);
                if (StrUtil.isBlankIfStr(redisToken) || !StrUtil.equals(redisToken, reqToken)) {
                    throw new TokenException("非法token,与redis中不一致");
                }
                // TODO 从redis获取用户信息
                User byId = userService.getById(uid);
                // TODO 将用户信息放入MyUsernamePasswordToken
                List<Long> permsByUserId = userRoleService.getPermsByUserId(uid);
                LoginUser loginUser = new LoginUser(byId, permsByUserId);
                if (loginUser.getUser().getStatus() == 1) {
                    redisTemplate.delete(adminUser + "login:" + uid);
                    throw new TokenException("账户被禁用,请重新登录");
                }
                MyUsernamePasswordToken myUsernamePasswordToken = new MyUsernamePasswordToken(loginUser, null,
                        loginUser.getAuthorities());
                // TODO 将认证的信息放入 SecurityContextHolder.getContext()
                SecurityContextHolder.getContext().setAuthentication(myUsernamePasswordToken);
                chain.doFilter(request, response);
                return;
            }
            throw new TokenException("非法token");
        } catch (TokenException exception) {
            JwtUnAuthorizedResponseAuthenticationEntryPoint point = new JwtUnAuthorizedResponseAuthenticationEntryPoint();
            point.commence(request, response, exception);
        }

    }

}

4. Security配置类

@Configuration
@EnableWebSecurity    // 添加 security 过滤器\
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    /**
     * token认证器
     */
    @Autowired
    IOrderUserService orderUserService;
    @Autowired
    IUserService userService;
    @Autowired
    MyJwtUtil myJwtUtil;
    @Autowired
    UserRoleService userRoleService;
    @Autowired
    RedisTemplate<String, String> redisTemplate;

    JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter(orderUserService, userService, myJwtUtil, userRoleService, redisTemplate);
    }

    /**
     * 放行路径配置类
     */
    @Autowired
    SecurityRelease securityRelease;

    /**
     * 放行路径
     */

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 获取AuthenticationManager(认证管理器),登录时认证使用
     *
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();
        return authenticationManager;
    }

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandlerImpl());
        return
                http.authorizeRequests(authorize -> authorize
                                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                                // 请求放开
                                .antMatchers(securityRelease.getReleaseUrl()).permitAll()
                                // 其他地址的访问均需验证权限
                                .anyRequest().authenticated()
                        ) // 基于 token,不需要 csrf
                        .csrf().disable()
                        .formLogin().disable()
                        // 基于 token,不需要 session
                        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                        .addFilterAt(wxLoginFilter(), UsernamePasswordAuthenticationFilter.class)
                        .addFilterAt(usernamePasswordFilter(), UsernamePasswordAuthenticationFilter.class)
                        .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                        .build();


    }

    /**
     * 配置跨源访问(CORS)
     *
     * @return
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }


    @Autowired
    WxLoginServiceImpl wxLoginService;
    @Autowired
    WxLoginUtilService wxLoginUtilService;
    @Autowired
    WxLoginSuccessHandler wxLoginSuccessHandler;

    // 添加微信用户登录拦截器
    WxLoginFilter wxLoginFilter() throws Exception {
        WxLoginFilter myUsernameFilter = new WxLoginFilter();
        ProviderManager providerManager = new ProviderManager(Collections.singletonList(wxLoginProvider()));
        myUsernameFilter.setAuthenticationManager(providerManager);
        myUsernameFilter.setAuthenticationSuccessHandler(wxLoginSuccessHandler);
        myUsernameFilter.setAuthenticationFailureHandler(new WxLoginErrorHandler());
        myUsernameFilter.setContinueChainBeforeSuccessfulAuthentication(true);
        return myUsernameFilter;
    }

    WxLoginProvider wxLoginProvider() {
        WxLoginProvider provider = new WxLoginProvider(wxLoginService, wxLoginUtilService);
        return provider;
    }

    @Autowired
    MyUsernamePasswordSuccessHandler myUsernamePasswordSuccessHandler;
    @Autowired
    MyUserDetailsService userDetailsService;

    /**
     * 添加用户登录拦截器
     *
     * @return
     */
    MyUsernamePasswordFilter usernamePasswordFilter() {
        MyUsernamePasswordFilter filter = new MyUsernamePasswordFilter();
        ProviderManager manager = new ProviderManager(Collections.singletonList(usernamePasswordProvider()));
        filter.setAuthenticationManager(manager);
        filter.setAuthenticationFailureHandler(new MyUsernamePasswordErrorHandler());
        filter.setAuthenticationSuccessHandler(myUsernamePasswordSuccessHandler);
        return filter;
    }


    MyUsernamePasswordProvider usernamePasswordProvider() {
        return new MyUsernamePasswordProvider(userDetailsService, passwordEncoder());
    }
}

4.yml 文件配置放行路径

my-security:
  releaseUrl:
    - /wxlogin/**
    - /user/login
    - /user/logout
    - /doc.html
    - /webjars/**
    - /swagger-resources/**
    - /v2/**
    - /wx-pay/**
    - /**

5. 放行路径配置类

/**
 * 2023/3/13
 * security 放行路径配置类
 *
 * @author cyh
 * 14:27
 */
@Configuration
@Data
@ConfigurationProperties(prefix = "my-security")
public class SecurityRelease {
    private String[] releaseUrl;
}
### 回答1: Spring Security提供了多种登录方式的支持,可以使用表单登录、Basic认证、OAuth2等方式进行身份验证。如果需要自定义多种登录方式,可以按照以下步骤进行: 1. 实现自定义的AuthenticationProvider AuthenticationProvider是Spring Security的一个核心接口,用于实现身份验证逻辑。通过实现自定义的AuthenticationProvider,可以实现多种不同的身份验证方式。 例如,可以实现一个LDAPAuthenticationProvider,用于基于LDAP的身份验证,或者实现一个SmsCodeAuthenticationProvider,用于基于短信验证码的身份验证。 2. 配置多个AuthenticationProvider 在Spring Security的配置文件中,可以通过配置多个AuthenticationProvider来支持多种登录方式。例如,可以同时配置一个基于表单登录的AuthenticationProvider和一个基于OAuth2的AuthenticationProvider。 3. 实现自定义的AuthenticationFilter AuthenticationFilter是Spring Security用于处理身份验证请求的过滤器。通过实现自定义的AuthenticationFilter,可以实现多种不同的身份验证方式。 例如,可以实现一个基于短信验证码的AuthenticationFilter,用于处理短信验证码登录请求。 4. 配置多个AuthenticationFilter 在Spring Security的配置文件中,可以通过配置多个AuthenticationFilter来支持多种登录方式。例如,可以同时配置一个基于表单登录的AuthenticationFilter和一个基于短信验证码的AuthenticationFilter。 总的来说,实现多种登录方式的关键在于实现自定义的AuthenticationProvider和AuthenticationFilter,并在Spring Security的配置文件中进行配置。 ### 回答2: Spring Security 提供了多种自定义登录方式的选项。以下是一些常见的方法: 1. 自定义用户名密码登录:可以使用 Spring Security 的表单登录功能,通过配置用户名和密码的输入框,实现用户名密码登录功能。 例如,可以通过配置 `formLogin()` 方法来实现: ```java protected void configure(HttpSecurity http) throws Exception { http .formLogin() .loginPage("/login") .usernameParameter("username") .passwordParameter("password") .defaultSuccessUrl("/home") .permitAll(); } ``` 2. 自定义第三方登录:可以使用 Spring Security OAuth2 来实现第三方登录,例如使用 Facebook、Google 或 Github 等社交媒体的账号进行登录Spring Security OAuth2 提供了很多集成第三方认证的实例代码,可以根据具体的需求进行自定义。 3. 自定义手机号码登录:可以通过继承 Spring Security 的 `AbstractAuthenticationProcessingFilter` 类来实现自定义手机号码登录。 可以在自定义的过滤器中验证手机号码,并进行认证逻辑。 4. 自定义单点登录(SSO):可以通过集成 Spring Security 的 `AuthenticationProvider` 接口来实现自定义的单点登录认证。 可以通过实现该接口的 `authenticate()` 方法来处理单点登录的逻辑。 这些只是一些常见的自定义登录方式的示例,根据具体的需求,可以结合 Spring Security 提供的各种功能和扩展点,灵活地进行自定义实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值