僅需四步,整合SpringSecurity+JWT實現登陸認證 !

SpringBoot實戰電商項目mall(25k+star)地址: https://github.com/macrozheng/mall

摘要

學習過個人mall項目的應該知道,mall-admin模塊是使用SpringSecurity+JWT來實現登陸認證的,而mall-portal模塊是使用的SpringSecurity基於Session的默認機制來實現登錄認證的。不少小夥伴都找不到mall-portal的登陸接口,最近我把這兩個模塊的登陸認證給統一了,都使用SpringSecurity+JWT的形式實現。
主要是經過把登陸認證的通用邏輯抽取到了mall-security模塊來實現的,下面咱們講講如何使用mall-security模塊來實現登陸認證,僅需四步便可。css

整合步驟

這裏咱們以 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中實現登陸和刷新token的接口:
/**
 * 會員登陸註冊管理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();
    }

}

項目源碼地址

https://github.com/macrozheng/mallhtml

公衆號

mall項目全套學習教程連載中,關注公衆號第一時間獲取。java

公衆號圖片

相關文章
相關標籤/搜索