GithubHelp home page GithubHelp logo

snailclimb / spring-security-jwt-guide Goto Github PK

View Code? Open in Web Editor NEW
1.3K 31.0 429.0 2.08 MB

从零入门 !Spring Security With JWT(含权限验证)后端部分代码。

Java 100.00%
springbooy jpa spring-security-jwt spring-security spring-security-5 register

spring-security-jwt-guide's People

Contributors

boynchan avatar dependabot[bot] avatar douzeping avatar sav7ng avatar snailclimb avatar zzh1991 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-security-jwt-guide's Issues

对于登录方面有一些疑问

我看您的代码,第一次登录时是在controller层对于密码和用户名进行检查.但是在SpringSecurity中似乎是通过过滤器去验证UsernamePasswordToken成功之后才到达controller层执行之后的请求.这样的话SpringSecurity的作用似乎就体现不出来了

关于配置context-paht为/api的配置

你这样配置了这个context-path为api,然后白名单里面又设置/api/auth/login放行,但是这个配置不会生效。
springsecurity默认匹配url的时候,不会携带项目名,也就是你的/api。
这里问题很大,我调试了很久才找到问题。不明白你测试的时候怎么会通过的,你登录的时候接口不会报错吗。

异常捕获

当重复注册时,并未按照设置的400报错,而是按照404。

循环依赖

最近在学习这个项目中的Spring Security,用其中的代码做了一个自己的项目,在线下用mvn packagejava -jar都运行正常,使用DevOps部署到线上出现了循环依赖的异常。(为了使idea生成下面的图,加了几处@Autowired注解)

  1. 要生成userController,就需要通过自动装配方式使用userService

    public class UserController {
     private final UserService userService;
  2. 其中userService需要bCryptPasswordEncoder,也是通过@Autowired方式装配

    public class UserService {
     @Autowired
     private final BCryptPasswordEncoder bCryptPasswordEncoder;
  3. bCryptPasswordEncodersecurityConfig中由注解@Bean制造出

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
       @Bean
       public BCryptPasswordEncoder bCryptPasswordEncoder() {
           return new BCryptPasswordEncoder();
       }

    bCrypt

  4. 在此之前,securityConfig需要得到userDetailsServiceImpl

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
     @Autowired
     UserDetailsServiceImpl userDetailsServiceImpl;
  5. userDetailsServiceImpl又需要使用userService来调用它的find方法

    @Service
     public class UserDetailsServiceImpl implements UserDetailsService {
    
        private final UserService userService;
    
        @Autowired
        public UserDetailsServiceImpl(UserService userService) {
            this.userService = userService;
        }
    
        @Override
        public UserDetails loadUserByUsername(String name) {
            User user = userService.find(name);
            return new JwtUser(user);
        }
    
      }

    UserDetailsServiceImpl

  6. 但此时的userService还没有制造好……于是有了循环依赖异常org.springframework.beans.factory.UnsatisfiedDependencyException --> UnsatisfiedDependencyException --> ... --> beans.factory.BeanCurrentlyInCreationException

image
IMG_1515

通过改写loadUserByUsername的方式,去除了UserDetailsServiceImplUserService的依赖,临时性地解决了这个异常。

token合法性问题

jwt生成的token是可以通过绑定的加密算法Hmac直接校验的,为什么要把token存在redis去校验,这样子与其他方案生成加密串作为token有什么区别呢?

post调试工具

你好,Guide哥。看到你的调试工具很特别,不像postman。能问下你使用的是什么调试工具吗?
not-have-enough-permission

按照你的方式自行搭建了一下无任何响应

过滤器和配置均搭配好,postman无论是post还是get,请求后没有任何响应,把SecurityConfig.configure(HttpSecurity http)中的代码注释掉 获取用户接口可以使用,但是注册就报403权限了

说明文档请修改一下

h2数据库页面访问地址 http://{host}:9333/h2-console/ 请修改为http://{host}:9333/api/h2-console/

"/auth/register"未定义解决

是"README.md"没更新吧,正确的注册地址是"/api/users/sign-up"
而且加了"/api"根路径配置,所以正确登录地址应该是"/api/auth/login"

jwt是否适合作为后台登录的token

后台肯定是希望用户登录一段时间没操作后失效了,如果有操作要续期,如果使用jwt的过期时间每次续期都要重新生成,如果用redis管理过期时间,那jwt就没有什么意义了

注册之后登陆的失败

错误:
at github.javaguide.springsecurityjwtguide.security.filter.JWTAuthenticationFilter.successfulAuthentication(JWTAuthenticationFilter.java:73) ~[classes/:na]
http 响应
{ "timestamp": "2019-10-14T01:23:26.399+0000", "status": 500, "error": "Internal Server Error", "message": "No message available", "path": "/auth/login" }

User实体里的updateFrom方法未给密码加密导致更新后以明文存储密码

    public void updateFrom(UserUpdateRequest userUpdateRequest) {
        if (Objects.nonNull(userUpdateRequest.getFullName())) {
            this.setFullName(userUpdateRequest.getFullName());
        }
        if (Objects.nonNull(userUpdateRequest.getPassword())) {
            this.setPassword(userUpdateRequest.getPassword());
        }
        if (Objects.nonNull(userUpdateRequest.getEnabled())) {
            this.setEnabled(userUpdateRequest.getEnabled());
        }
    }

SpringSecurity介绍.md文件问题

SpringSecurity介绍.md文件中给出的配置类SecurityConfig和项目代码中的SecurityConfig内容不一样啊。

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    UserDetailsServiceImpl userDetailsServiceImpl;

    /**
     * 密码编码器
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService createUserDetailsService() {
        return userDetailsServiceImpl;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 设置自定义的userDetailsService以及密码编码器
        auth.userDetailsService(userDetailsServiceImpl).passwordEncoder(bCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                // 禁用 CSRF
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/auth/login").permitAll()
                // 指定路径下的资源需要验证了的用户才能访问
                .antMatchers("/api/**").authenticated()
                .antMatchers(HttpMethod.DELETE, "/api/**").hasRole("ADMIN")
                // 其他都放行了
                .anyRequest().permitAll()
                .and()
                //添加自定义Filter
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                // 不需要session(不创建会话)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 授权异常处理
                .exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint())
                .accessDeniedHandler(new JWTAccessDeniedHandler());

    }

}
/**
 * @author shuang.kou Saving
 * @version 1.1
 * @date 2020.11.28 14:16
 * @description Spring Security配置类
 **/
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final StringRedisTemplate stringRedisTemplate;

    public SecurityConfiguration(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 密码编码器
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors(withDefaults())
                // 禁用 CSRF
                .csrf().disable()
                .authorizeRequests()
                // 指定的接口直接放行
                // swagger
                .antMatchers(SecurityConstants.SWAGGER_WHITELIST).permitAll()
                .antMatchers(SecurityConstants.H2_CONSOLE).permitAll()
                .antMatchers(HttpMethod.POST, SecurityConstants.SYSTEM_WHITELIST).permitAll()
                // 其他的接口都需要认证后才能请求
                .anyRequest().authenticated()
                .and()
                //添加自定义Filter
                .addFilter(new JwtAuthorizationFilter(authenticationManager(), stringRedisTemplate))
                // 不需要session(不创建会话)
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 授权异常处理
                .exceptionHandling().authenticationEntryPoint(new JwtAuthenticationEntryPoint())
                .accessDeniedHandler(new JwtAccessDeniedHandler());
        // 防止H2 web 页面的Frame 被拦截
        http.headers().frameOptions().disable();
    }

    /**
     * Cors配置优化
     **/
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        org.springframework.web.cors.CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(singletonList("*"));
        // configuration.setAllowedOriginPatterns(singletonList("*"));
        configuration.setAllowedHeaders(singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "DELETE", "PUT", "OPTIONS"));
        configuration.setExposedHeaders(singletonList(SecurityConstants.TOKEN_HEADER));
        configuration.setAllowCredentials(false);
        configuration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
//        DaoAuthenticationProvider
        return source;
    }

}

登录URL的问题

为什么登录是localhost:9333/api/auth/login而不是localhost:9333/auth/login?
在SecurityConfig 中的代码是这样的
.antMatchers(HttpMethod.POST, "/auth/login").permitAll()
// 指定路径下的资源需要验证了的用户才能访问
.antMatchers("/api/**").authenticated()
为什么前面还需要加/api?求解惑。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.