springsecurity+oauth2自定义生成jwt token

最近在重构公司用户中心,所以自己对oauth有了一点新的理解,因为公司业务形态需求使用默认的四种模式都不太合适,所以选择自定义实现token的下发,本文需要对oauth流程有一定理解可能会容易看一点,只是主要流程的部分实现。

时序图

springsecurity+oauth2自定义生成jwt token_第1张图片

/**
 * 自定义登录验证filter
 */
@Order(10)
public class LoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private static final String SPRING_SECURITY_FORM_UCENTER_KEY = "userCode";


    @Getter
    @Setter
    private String loginParameter = SPRING_SECURITY_FORM_UCENTER_KEY;

    @Getter
    @Setter
    private boolean postOnly = true;

    @Getter
    @Setter
    private AuthenticationEventPublisher eventPublisher;

    @Getter
    @Setter
    private AuthenticationEntryPoint authenticationEntryPoint;

    public LoginAuthenticationFilter() {
        super(new AntPathRequestMatcher(SecurityConstants.UCENTER_TOKEN_URL, "POST"));
    }

    @Override
    @SneakyThrows
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
        if (postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String login = obtainLogin(request);
        if (login == null) {
            MessageSourceUtil messageSourceUtil = SpringUtils.getBean("messageSourceUtil");
            throw BusinessException.get(ExceptionConstant.SYSTEM_ERROR, messageSourceUtil.getMessage(I18nConstant.SystemMessage.SYSTEM_CHECKCODE_EMPTY));
        }
        login = login.trim();
        LoginAuthenticationToken loginAuthenticationToken = new LoginAuthenticationToken(login, request.getParameter("password"));
        setDetails(request, loginAuthenticationToken);
        Authentication authResult = null;
        try {
            authResult = this.getAuthenticationManager().authenticate(loginAuthenticationToken);
            logger.debug("Authentication success: " + authResult);
            SecurityContextHolder.getContext().setAuthentication(authResult);
        } catch (Exception failed) {
            SecurityContextHolder.clearContext();
            logger.debug("Authentication request failed: " + failed);
            eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed), new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
            try {
                authenticationEntryPoint.commence(request, response, new UsernameNotFoundException(failed.getMessage(), failed));
            } catch (Exception e) {
                logger.error("authenticationEntryPoint handle error:{}", failed);
            }
        }

        return authResult;
    }

    private String obtainMobile(HttpServletRequest request) {
        return request.getParameter(loginParameter);
    }

    private void setDetails(HttpServletRequest request, LoginAuthenticationToken authRequest) {
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
    } 
}

/**
 * 自定义登录校验逻辑
 */
@Slf4j
public class LoginAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    MessageSourceUtil messageSourceUtil;
    private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private UserDetailsChecker detailsChecker = new PreAuthenticationChecks();
    @Getter
    @Setter
    private SysUserService sysUserService;

    @Override
    @SneakyThrows
    public Authentication authenticate(Authentication authentication) {
        LoginAuthenticationToken loginAuthenticationToken = (LoginAuthenticationToken) authentication;
        String userCode = loginAuthenticationToken.getPrincipal().toString();
        UserDetails userDetails = sysUserService.loadUserByUCenter(userCode);
        if (userDetails == null) {
            log.debug("Authentication failed: no credentials provided");
            throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
        }
        String frontPassword = ((LoginAuthenticationToken) authentication).getPassword();
        if (StringUtils.isNotBlank(frontPassword)) {
            String dePassword = RSAUtils.decrypt(frontPassword);
            if (StringUtils.isNotBlank(dePassword)) {
                frontPassword = dePassword;
            } else {
                throw BusinessException.get(ExceptionConstant.SYSTEM_ERROR, messageSourceUtil.getMessage(I18nConstant.SystemMessage.PASSWORD_EMPTY));
            }
        }
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        if (!passwordEncoder.matches(frontPassword, userDetails.getPassword())) {
            throw BusinessException.get(ExceptionConstant.SYSTEM_ERROR, messageSourceUtil.getMessage(I18nConstant.SystemMessage.USERCODE_OR_PASSWORD_ERROR));
        }
        // 检查账号状态
        detailsChecker.check(userDetails);

        LoginAuthenticationToken authenticationToken = new LoginAuthenticationToken(userDetails, userDetails.getAuthorities());
        authenticationToken.setDetails(loginAuthenticationToken.getDetails());
        return authenticationToken;
    }

    @Override
    public boolean supports(Class authentication) {
        return LoginAuthenticationToken.class.isAssignableFrom(authentication);
    }

}

/**
 * 自定义登录配置入口
 */
@Getter
@Setter
public class LoginSecurityConfigurer extends SecurityConfigurerAdapter, HttpSecurity> {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private AuthenticationEventPublisher defaultAuthenticationEventPublisher;

    @Autowired
    private AuthenticationSuccessHandler loginSuccessHandler;

    @Autowired
    private SysUserService sysUserService;

    @Override
    @SneakyThrows
    public void configure(HttpSecurity http) {
        LoginAuthenticationFilter loginAuthenticationFilter = new LoginAuthenticationFilter();
        loginAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        loginAuthenticationFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
        loginAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
        loginAuthenticationFilter.setAuthenticationEntryPoint(new ResourceAuthExceptionEntryPoint(objectMapper));
        LoginAuthenticationProvider loginAuthenticationProvider = new LoginAuthenticationProvider();
        loginAuthenticationProvider.setSysUserService(sysUserService);
        http.authenticationProvider(loginAuthenticationProvider).addFilterAfter(loginAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

}

/**
 * 认证成功事件监听器
 */
@Component
public class AuthenticationSuccessEventListener implements ApplicationListener {

    @Autowired(required = false)
    private AuthenticationSuccessHandler successHandler;


    /**
     * Handle an application event.
     *
     * @param event the event to respond to
     */
    @Override
    public void onApplicationEvent(AuthenticationSuccessEvent event) {
        Authentication authentication = (Authentication) event.getSource();
        if (successHandler != null && isUserAuthentication(authentication)) {
            ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = requestAttributes.getRequest();
            HttpServletResponse response = requestAttributes.getResponse();

            successHandler.handle(authentication, request, response);
        }
    }

    private boolean isUserAuthentication(Authentication authentication) {
        return authentication.getPrincipal() instanceof ImileUser || CollUtil.isNotEmpty(authentication.getAuthorities());
    }

}

/**
 * 自定义登录成功,返回oauth token
 */
@Slf4j
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    private static final String BASIC_ = "Basic ";
    @Autowired
    RedisHolderService redisHolderService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Lazy
    @Autowired
    private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;

    /**
     * Called when a user has been successfully authenticated. 调用spring security oauth API
     * 生成 oAuth2AccessToken
     *
     * @param request        the request which caused the successful authentication
     * @param response       the response
     * @param authentication the Authentication object which was created during
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) {
        String header = request.getHeader(HttpHeaders.AUTHORIZATION);

        try {
            String[] tokens = AuthUtils.extractAndDecodeHeader(header);
            assert tokens.length == 2;
            String clientId = tokens[0];

            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);

            // 校验secret
            if (!passwordEncoder.matches(tokens[1], clientDetails.getClientSecret())) {
                throw new InvalidClientException("Given client ID does not match authenticated client");

            }

            TokenRequest tokenRequest = new TokenRequest(MapUtil.newHashMap(), clientId, clientDetails.getScope(), "ucenter");

            // 校验scope
            new DefaultOAuth2RequestValidator().validateScope(tokenRequest, clientDetails);
            OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
            OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
            OAuth2AccessToken oAuth2AccessToken = defaultAuthorizationServerTokenServices.createAccessToken(oAuth2Authentication);
            String token = oAuth2AccessToken.getValue();
            log.info("获取token 成功:{}", oAuth2AccessToken.getValue());
            //写入redis
            String locale = StringUtils.isEmpty(request.getParameter("lang")) ? Locale.CHINA.toString() : request.getParameter("lang");
            String timeZone = StringUtils.isEmpty(request.getParameter("timeZone")) ? DEFAULT_TIME_ZONE : request.getParameter("timeZone");
            
            response.setCharacterEncoding(CharsetUtil.UTF_8);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            PrintWriter printWriter = response.getWriter();
            printWriter.append(objectMapper.writeValueAsString(oAuth2AccessToken));
        } catch (IOException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }

    }

你可能感兴趣的:(java,jwt,http,shiro,spring)