Всего четыре шага для интеграции SpringSecurity+JWT для аутентификации при входе!

Spring Boot
Всего четыре шага для интеграции SpringSecurity+JWT для аутентификации при входе!

Адрес фактического центра электронной коммерции SpringBoot (25k+star):GitHub.com/macro-positive/…

Резюме

Те, кто изучал мой проект торгового центра, должны знать,mall-adminМодуль использует SpringSecurity+JWT для реализации аутентификации при входе в систему иmall-portalМодуль использует механизм Spring Security по умолчанию, основанный на сеансе, для реализации аутентификации при входе. Многие друзья не могут найтиmall-portalИнтерфейс входа в систему, недавно я объединил аутентификацию этих двух модулей, и они реализованы в виде SpringSecurity+JWT. В основном путем извлечения общей логики аутентификации при входе вmall-securityмодуль для достижения, давайте поговорим о том, как использоватьmall-securityмодуля для аутентификации при входе в систему требуется всего четыре шага.

Шаги интеграции

Здесь мы беремmall-portalВозьмите преобразование в качестве примера, чтобы объяснить, как его достичь.

  • Первый шаг — добавить модули, требующие аутентификации при входе.mall-securityполагаться:
<dependency>
    <groupId>com.macro.mall</groupId>
    <artifactId>mall-security</artifactId>
</dependency>
  • Второй шаг, добавьте класс конфигурации MallSecurityConfig, наследуйтеmall-securityКонфигурация SecurityConfig в , и настройте класс реализации интерфейса UserDetailsService для получения сведений о пользователе для входа:
/**
 * mall-security模块相关配置
 * Created by macro on 2019/11/5.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class MallSecurityConfig extends SecurityConfig {

    @Autowired
    private UmsMemberService memberService;

    @Bean
    public UserDetailsService userDetailsService() {
        //获取登录用户信息
        return username -> memberService.loadUserByUsername(username);
    }
}
  • Третий шаг — настроить пути к ресурсам, не требующим защиты безопасности, в application.yml:
secure:
  ignored:
    urls: #安全路径白名单
      - /swagger-ui.html
      - /swagger-resources/**
      - /swagger/**
      - /**/v2/api-docs
      - /**/*.js
      - /**/*.css
      - /**/*.png
      - /**/*.ico
      - /webjars/springfox-swagger-ui/**
      - /druid/**
      - /actuator/**
      - /sso/**
      - /home/**
  • Четвертый шаг — реализовать интерфейс для входа и обновления токена в UmsMemberController:
/**
 * 会员登录注册管理Controller
 * Created by macro on 2018/8/3.
 */
@Controller
@Api(tags = "UmsMemberController", description = "会员登录注册管理")
@RequestMapping("/sso")
public class UmsMemberController {
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Autowired
    private UmsMemberService memberService;

    @ApiOperation("会员登录")
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult login(@RequestParam String username,
                              @RequestParam String password) {
        String token = memberService.login(username, password);
        if (token == null) {
            return CommonResult.validateFailed("用户名或密码错误");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", token);
        tokenMap.put("tokenHead", tokenHead);
        return CommonResult.success(tokenMap);
    }

    @ApiOperation(value = "刷新token")
    @RequestMapping(value = "/refreshToken", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult refreshToken(HttpServletRequest request) {
        String token = request.getHeader(tokenHeader);
        String refreshToken = memberService.refreshToken(token);
        if (refreshToken == null) {
            return CommonResult.failed("token已经过期!");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token", refreshToken);
        tokenMap.put("tokenHead", tokenHead);
        return CommonResult.success(tokenMap);
    }
}

Принцип реализации

После инкапсуляции кода SpringSecurity+JWT в общий модуль, он может использоваться другими модулями, требующими аутентификации входа.Давайте посмотрим, как это реализовано.Сначала давайте посмотримmall-securityструктура каталогов.

Структура каталогов

mall-security
├── component
|    ├── JwtAuthenticationTokenFilter -- JWT登录授权过滤器
|    ├── RestAuthenticationEntryPoint -- 自定义返回结果:未登录或登录过期
|    └── RestfulAccessDeniedHandler -- 自定义返回结果:没有权限访问时
├── config
|    ├── IgnoreUrlsConfig -- 用于配置不需要安全保护的资源路径
|    └── SecurityConfig -- SpringSecurity通用配置
└── util
     └── JwtTokenUtil -- JWT的token处理工具类

какие изменения были внесены

На самом деле я также добавил два класса, IgnoreUrlsConfig, который используется для получения путей к ресурсам, не требующим защиты безопасности, из application.yml. SecurityConfig абстрагирует часть общей конфигурации SpringSecurity.

  • Код в IgnoreUrlsConfig:
/**
 * 用于配置不需要保护的资源路径
 * Created by macro on 2018/11/5.
 */
@Getter
@Setter
@ConfigurationProperties(prefix = "secure.ignored")
public class IgnoreUrlsConfig {

    private List<String> urls = new ArrayList<>();

}
  • Код в SecurityConfig:
/**
 * 对SpringSecurity的配置的扩展,支持自定义白名单资源路径和查询用户逻辑
 * Created by macro on 2019/11/5.
 */
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //不需要保护的资源路径允许访问
        for (String url : ignoreUrlsConfig().getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();
        // 任何请求需要身份认证
        registry.and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                // 关闭跨站请求防护及不使用session
                .and()
                .csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 自定义权限拒绝处理类
                .and()
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())
                .authenticationEntryPoint(restAuthenticationEntryPoint())
                // 自定义权限拦截器JWT过滤器
                .and()
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

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

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
        return new RestfulAccessDeniedHandler();
    }

    @Bean
    public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
        return new RestAuthenticationEntryPoint();
    }

    @Bean
    public IgnoreUrlsConfig ignoreUrlsConfig() {
        return new IgnoreUrlsConfig();
    }

    @Bean
    public JwtTokenUtil jwtTokenUtil() {
        return new JwtTokenUtil();
    }

}

Адрес исходного кода проекта

GitHub.com/macro-positive/…

публика

проект торгового центраПолный набор учебных пособий сериализуется,Обратите внимание на публичный аккаунтПолучите это прямо сейчас.

公众号图片