security实现多种登录方式 1

Security 多种方式登录

1.Security 介绍

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

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;
        }
    }
    
    
  2. 自定义UserDetails

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class LoginUser implements UserDetails, Serializable {
        private static final long serialVersionUID = -53711580203962796L;
    
        private User user;
        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;
        }
    }
    
    
  3. 自定义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);
        }
    }
    
    
  2. 微信登录的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;

    /**
     * 获取数据库用户信息
     *
     * @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));
        }
    }
    
    
  2. 失败返回

    /**
     * 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拦截器

    /**
     * 2023/2/26
     *
     * @author cyh
     * 17:18
     */
    @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;
        /**
         * jwt 工具
         */
        private MyJwtUtil myJwtUtil;
        /**
         * 用户权限
         */
        private UserRoleService userRoleService;
    
    
        public JwtAuthenticationTokenFilter(IOrderUserService orderUserService, IUserService userService, MyJwtUtil myJwtUtil, UserRoleService userRoleService, RedisTemplate<String, String> redisTemplate) {
            this.orderUserService = orderUserService;
            this.userService = userService;
            this.myJwtUtil = myJwtUtil;
            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;
            }
            // 判断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");
            }
            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");
                }
                // 判断获取的用户存不存在
                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");
                }
                // 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");
        }
    
    }
    

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;
}

3. 权限设置

  1. 数据库权限点保存

    ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aWa6zeeH-1679449812207)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322093826605.png)]

  2. 登录返回权限点

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mb8MtsDm-1679449812208)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322094029525.png)]

  3. 每次请求加载自定义Token并加载权限点

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QkccC3If-1679449812209)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322094147211.png)]

  4. 在方法前权限点验证用户权限 — 配置类开启方法级别的权限认证

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hb9Wn6uw-1679449812209)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322094236312.png)]

  1. 自定义权限点的转换

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IpMzvhOS-1679449812209)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322094404170.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zIKK617J-1679449812210)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230322094420956.png)]
rdProvider usernamePasswordProvider() {
return new MyUsernamePasswordProvider(userDetailsService, passwordEncoder());
}
}


### **4.yml** 文件配置放行路径

```java
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;
}

3. 权限设置

  1. 数据库权限点保存

    ​ [外链图片转存中…(img-aWa6zeeH-1679449812207)]

  2. 登录返回权限点

    [外链图片转存中…(img-Mb8MtsDm-1679449812208)]

  3. 每次请求加载自定义Token并加载权限点

    [外链图片转存中…(img-QkccC3If-1679449812209)]

  4. 在方法前权限点验证用户权限 — 配置类开启方法级别的权限认证

[外链图片转存中…(img-Hb9Wn6uw-1679449812209)]

  1. 自定义权限点的转换

    [外链图片转存中…(img-IpMzvhOS-1679449812209)]

[外链图片转存中…(img-zIKK617J-1679449812210)]

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答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 提供的各种功能和扩展点,灵活地进行自定义实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值