Springboot+jjwt+security restfulインタフェースのステータス認証なしを完璧に解決


マイクロサービスが盛んに行われている今、wsdlのようなインタフェースを提供していると、low的な感じがします.本稿ではrestfulインタフェースの利点を議論せず,restfulを使用する際の権限制御の問題を解決することを目的とする.
Springboot自体はすでにspring securityのサポートを提供しており、一部のインタフェースを実装(または書き換える)して個性的な設定を実現すればいいだけです.本文は分かりやすくて、原理に深く入り込んでいません(後で文章は、必要な仲間が少し待っています~~~).考え方:1.spring securityによるライセンスブロック操作2.jwtによりユーザ情報に基づいてtokenを生成する後に呼び出す.生成したtokenをHttpServeretResponseヘッダ情報に4.使用時にresponseヘッダからtokenを取得しrequestヘッダにバックグラウンドに提出して認証すればよい.デフォルトタイムアウト10日
一、pom
慣例はやはりまずpomに行きます.pomは本プロジェクトがどんなものを使ったのかを直感的に見ることができるので、私のこのプロジェクトは多くのバッグを使っています.ここにはコアのいくつかが貼られています.他の大部分は自動的に引用されます.
       <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-webartifactId>
            <scope>providedscope>
        dependency>
        <dependency>
            <groupId>org.springframework.securitygroupId>
            <artifactId>spring-security-configartifactId>
            <scope>providedscope>
        dependency>

        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
        dependency>

二、登録フィルタ
私たちはバックプッシュ法を採用して、何を使って何を探しているのか、これもXPプログラミングの思想に合っていて、多く(使わない)コードを書かないで、認証をする以上、すべてのブロックする必要がある要求を処理するためにフィルタが必要であることは明らかです.U s e r a m e P a s s s w o r d AuthenticationFilterはsecurityが自分で提供したフィルタで、私たちはその中の成功方法(successfulAuthentication)を書き直して私たちの論理を処理します.もちろん、自分の状況によって、例えばログイン失敗処理、書き換え(unsuccessfulAuthentication)すればいいです.1.正常なコールバックには、jwtのメカニズムを借りてtokenを生成し、後のログイン許可のために使用するtoken認証処理クラスというTokenAuthenticationHandlerが使用される.2.responseヘッダ情報にパラメータ「Authorization」、値「Bearer」+tokenを入れる値
package com.mos.eboot.tools.jwt;

import com.mos.eboot.tools.util.FastJsonUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter{

    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    private AuthenticationSuccessHandler successHandler;

    public JWTLoginFilter() {
    }

    public JWTLoginFilter(AuthenticationManager authManager) {
        setAuthenticationManager(authManager);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException {
        TokenAuthenticationHandler tokenAuthenticationHandler = new TokenAuthenticationHandler();
        Object obj = auth.getPrincipal();
        if(obj != null) {
            UserDetails userDetails = (UserDetails)obj;
            String token = tokenAuthenticationHandler.generateToken(FastJsonUtils.toJSONNoConfig(userDetails));
            res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + token);
        }

        if(successHandler != null) {
            successHandler.onAuthenticationSuccess(req, res, auth);
        }
    }

    public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
        this.successHandler = successHandler;
    }
}

JWTAuthenticationToken
package com.mos.eboot.tools.jwt;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;

import static java.util.Collections.emptyList;

public class JWTAuthenticationToken extends UsernamePasswordAuthenticationToken{

    private static final long serialVersionUID = 1L;

    public JWTAuthenticationToken(Object principal) {
        super(principal,null,emptyList());
    }

    @Override
    public Object getCredentials() {
        return super.getCredentials();
    }

    @Override
    public Object getPrincipal() {
        return super.getPrincipal();
    }

}

TokenAuthenticationHandler
package com.mos.eboot.tools.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author    
 */
public class TokenAuthenticationHandler implements Serializable {

    private static final long serialVersionUID = 1L;

    private static final String CLAIM_KEY_CREATED = "created";
    private static final String CLAIM_KEY_SUBJECT = "subject";

    private static final String DEFAULT_SECRET = "eboot@secret";
    private static final Long DEFAULT_EXPIRATION = 864000L;

    private String secret = DEFAULT_SECRET;
    private Long EXPIRATION = DEFAULT_EXPIRATION;

    public TokenAuthenticationHandler() {

    }

    public String getSubjectFromToken(String token) {
        String subject;
        try {
            final Claims claims = getClaimsFromToken(token);
            subject = claims.get(CLAIM_KEY_SUBJECT).toString();
        } catch (Exception e) {
            subject = null;
        }
        return subject;
    }


    private Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + EXPIRATION * 1000);
    }

    public String generateToken(String subject) {
        Map claims = new HashMap();
        claims.put(CLAIM_KEY_CREATED, new Date());
        claims.put(CLAIM_KEY_SUBJECT, subject);
        return generateToken(claims);
    }

    String generateToken( Map claims) {
        return Jwts.builder().setClaims(claims).setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret).compact();
    }

}

三、認証
Authorizationを最初から情報から取り出し、個人情報を解析します.個人情報が空でない場合は、個人情報を暗号化してから権限ドメインに入れます.
package com.mos.eboot.tools.jwt;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class JWTAuthenticationFilter extends GenericFilterBean {

    static final String HEADER_STRING = "Authorization";
    static final String TOKEN_PREFIX = "Bearer";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest)request;

        String token = req.getHeader(HEADER_STRING);
        if(StringUtils.isNotBlank(token) && token.startsWith(TOKEN_PREFIX)) {
            TokenAuthenticationHandler tokenAuthenticationHandler = new TokenAuthenticationHandler();
            String subject = tokenAuthenticationHandler.getSubjectFromToken(token.replace(TOKEN_PREFIX, ""));
            if(StringUtils.isNotBlank(subject)) {
                SecurityContextHolder.getContext().setAuthentication(new JWTAuthenticationToken(subject));
            }
        }
        filterChain.doFilter(request,response);
    }

}

四、呼び出し
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable().authorizeRequests().antMatchers("/**").authenticated()
                .antMatchers(HttpMethod.POST, "/login").permitAll().anyRequest().permitAll().and()
                .addFilterBefore(loginFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

//       
    @Bean
    public JWTLoginFilter loginFilter() throws Exception {
        JWTLoginFilter loginFilter = new JWTLoginFilter(authenticationManager());
        loginFilter.setSuccessHandler(loginAuthenticationSuccessHandler);
        loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
            response.setContentType("application/json");
            response.getWriter().write(FastJsonUtils
                    .toJSONString(new ResultModel(ResultStatus.FAIL.getCode(), exception.getMessage())));
        });
        return loginFilter;
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        authenticationProvider.setHideUserNotFoundExceptions(false);
        return authenticationProvider;
    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        return new UserService();
    }

//        
    @Bean
    public Md5PasswordEncoder passwordEncoder() {
        Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
        passwordEncoder.setIterations(1);
        return passwordEncoder;
    }

UserDetailsServiceインタフェースを実装し、独自のユーザー登録方法実装クラスを定義する
package com.mos.eboot.api.config.support;

import com.mos.eboot.api.platform.api.ISysUserService;
import com.mos.eboot.platform.entity.SysUser;
import com.mos.eboot.tools.result.ResultModel;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author    
 */
@Service("userService")
public class UserService implements IUserService {

    @Resource
    private ISysUserService sysUserService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return sysUserService.getByUsername(username);
    }
}

基本以上で基本的な無状態認証ができますが、権限のカスタマイズなど、もっと深く理解する必要がある場合は、私の後の文章に注目してください.......