当前位置: 首页>后端>正文

Spring Boot 2 + Spring Security 5 + JWT 的 Restful 简易教程

鍑嗗

寮€濮嬫湰鏁欑▼鐨勬椂鍊欏笇鏈涘涓嬮潰鐭ヨ瘑鐐硅繘琛岀矖鐣ョ殑浜嗚В銆?/p>

  • 鐭ラ亾 JWT 鐨勫熀鏈蹇?/li>
  • 浜嗚В杩?Spring Security

鏈」鐩腑 JWT 瀵嗛挜鏄娇鐢ㄧ敤鎴疯嚜宸辩殑鐧诲叆瀵嗙爜锛岃繖鏍锋瘡涓€涓?token 鐨勫瘑閽ラ兘涓嶅悓锛岀浉瀵规瘮杈冨畨鍏ㄣ€?/p>

澶т綋鎬濊矾锛?/h3>

鐧诲叆锛?/strong>

  1. POST 鐢ㄦ埛鍚嶅瘑鐮佸埌 \login
  2. 璇锋眰鍒拌揪 JwtAuthenticationFilter 涓殑 attemptAuthentication() 鏂规硶锛岃幏鍙?request 涓殑 POST 鍙傛暟锛屽寘瑁呮垚涓€涓?UsernamePasswordAuthenticationToken 浜や粯缁?AuthenticationManager 鐨?authenticate() 鏂规硶杩涜閴存潈銆?/li>
  3. AuthenticationManager 浼氫粠 CachingUserDetailsService 涓煡鎵剧敤鎴蜂俊鎭紝骞朵笖鍒ゆ柇璐﹀彿瀵嗙爜鏄惁姝g‘銆?/li>
  4. 濡傛灉璐﹀彿瀵嗙爜姝g‘璺宠浆鍒?JwtAuthenticationFilter 涓殑 successfulAuthentication() 鏂规硶锛屾垜浠繘琛岀鍚嶏紝鐢熸垚 token 杩斿洖缁欑敤鎴枫€?/li>
  5. 璐﹀彿瀵嗙爜閿欒鍒欒烦杞埌 JwtAuthenticationFilter 涓殑 unsuccessfulAuthentication() 鏂规硶锛屾垜浠繑鍥為敊璇俊鎭鐢ㄦ埛閲嶆柊鐧诲叆銆?/li>

璇锋眰閴存潈锛?/strong>

璇锋眰閴存潈鐨勪富瑕佹€濊矾鏄垜浠細浠庤姹備腑鐨?Authorization 瀛楁鎷垮彇 token锛屽鏋滀笉瀛樺湪姝ゅ瓧娈电殑鐢ㄦ埛锛孲pring Security 浼氶粯璁や細鐢?AnonymousAuthenticationToken() 鍖呰瀹冿紝鍗充唬琛ㄥ尶鍚嶇敤鎴枫€?/p>

  1. 浠绘剰璇锋眰鍙戣捣
  2. 鍒拌揪 JwtAuthorizationFilter 涓殑 doFilterInternal() 鏂规硶锛岃繘琛岄壌鏉冦€?/li>
  3. 濡傛灉閴存潈鎴愬姛鎴戜滑鎶婄敓鎴愮殑 Authentication 鐢?SecurityContextHolder.getContext().setAuthentication() 鏀惧叆 Security锛屽嵆浠h〃閴存潈瀹屾垚銆傛澶勫浣曢壌鏉冪敱鎴戜滑鑷繁浠g爜缂栧啓锛屽悗搴忎細璇︾粏璇存槑銆?/li>

鍑嗗 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.inlighting</groupId>
    <artifactId>spring-boot-security-jwt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-security-jwt</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- JWT 鏀寔 -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.2</version>
        </dependency>

        <!-- cache 鏀寔 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- cache 鏀寔 -->
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>

        <!-- cache 鏀寔 -->
        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>

        <!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.3.0</version>
        </dependency>

        <!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.3.0</version>
        </dependency>

        <!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

pom.xml 閰嶇疆鏂囦欢杩欏潡娌℃湁浠€涔堝ソ璇寸殑锛屼富瑕佽鏄庝笅闈㈢殑鍑犱釜渚濊禆锛?/p>

<!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
<dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
  <version>2.3.0</version>
</dependency>

<!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-impl</artifactId>
  <version>2.3.0</version>
</dependency>

<!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-core</artifactId>
  <version>2.3.0</version>
</dependency>

<!-- ehcache 璇诲彇 xml 閰嶇疆鏂囦欢浣跨敤 -->
<dependency>
  <groupId>javax.activation</groupId>
  <artifactId>activation</artifactId>
  <version>1.1.1</version>
</dependency>

鍥犱负 ehcache 璇诲彇 xml 閰嶇疆鏂囦欢鏃朵娇鐢ㄤ簡杩欏嚑涓緷璧栵紝鑰岃繖鍑犱釜渚濊禆浠?JDK 9 寮€濮嬫椂鏄€夐厤妯″潡锛屾墍浠ラ珮鐗堟湰鐨勭敤鎴烽渶瑕佹坊鍔犺繖鍑犱釜渚濊禆鎵嶈兘姝e父浣跨敤銆?/p>

鍩虹宸ヤ綔鍑嗗

鎺ヤ笅鏉ュ噯澶囦笅鍑犱釜鍩虹宸ヤ綔锛屽氨鏄柊寤轰釜瀹炰綋銆佹ā鎷熶釜鏁版嵁搴擄紝鍐欎釜 JWT 宸ュ叿绫昏繖绉嶅熀纭€鎿嶄綔銆?/p>

UserEntity.java

鍏充簬 role 涓轰粈涔堜娇鐢?GrantedAuthority 璇存槑涓嬶細鍏跺疄鏄负浜嗙畝鍖栦唬鐮侊紝鐩存帴鐢ㄤ簡 Security 鐜版垚鐨?role 绫伙紝瀹為檯椤圭洰涓垜浠偗瀹氳鑷繁杩涜澶勭悊锛屽皢鍏惰浆鎹负 Security 鐨?role 绫汇€?/p>

public class UserEntity {

    public UserEntity(String username, String password, Collection<extends GrantedAuthority> role) {
        this.username = username;
        this.password = password;
        this.role = role;
    }

    private String username;

    private String password;

    private Collection<extends GrantedAuthority> role;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Collection<extends GrantedAuthority> getRole() {
        return role;
    }

    public void setRole(Collection<extends GrantedAuthority> role) {
        this.role = role;
    }
}

ResponseEntity.java

鍓嶅悗绔垎绂讳负浜嗘柟渚垮墠绔垜浠缁熶竴 json 鐨勮繑鍥炴牸寮忥紝鎵€浠ヨ嚜瀹氫箟涓€涓?ResponseEntity.java銆?/p>

public class ResponseEntity {

    public ResponseEntity() {
    }

    public ResponseEntity(int status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    private int status;

    private String msg;

    private Object data;

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
}

Database.java

杩欓噷鎴戜滑浣跨敤涓€涓?HashMap 妯℃嫙浜嗕竴涓暟鎹簱锛屽瘑鐮佹垜宸茬粡棰勫厛鐢?Bcrypt 鍔犲瘑杩囦簡锛岃繖涔熸槸 Spring Security 瀹樻柟鎺ㄨ崘鐨勫姞瀵嗙畻娉曪紙MD5 鍔犲瘑宸茬粡鍦?Spring Security 5 涓绉婚櫎浜嗭紝涓嶅畨鍏級銆?/p>

鐢ㄦ埛鍚?/th> 瀵嗙爜 鏉冮檺
jack jack123 瀛?Bcrypt 鍔犲瘑鍚?/td> ROLE_USER
danny danny123 瀛?Bcrypt 鍔犲瘑鍚?/td> ROLE_EDITOR
smith smith123 瀛?Bcrypt 鍔犲瘑鍚?/td> ROLE_ADMIN
@Component
public class Database {
    private Map<String, UserEntity> data = null;
    
    public Map<String, UserEntity> getDatabase() {
        if (data == null) {
            data = new HashMap<>();

            UserEntity jack = new UserEntity(
                    "jack",
                    "a$AQol1A.LkxoJ5dEzS5o5E.QG9jD.hncoeCGdVaMQZaiYZ98V/JyRq",
                    getGrants("ROLE_USER"));
            UserEntity danny = new UserEntity(
                    "danny",
                    "anMJR6r7lvh9H2INtM2vtuA156dHTcQUyU.2Q2OK/7LwMd/I.HM12",
                    getGrants("ROLE_EDITOR"));
            UserEntity smith = new UserEntity(
                    "smith",
                    "a$E86mKigOx1NeIr7D6CJM3OQnWdaPXOjWe4OoRqDqFgNgowvJW9nAi",
                    getGrants("ROLE_ADMIN"));
            data.put("jack", jack);
            data.put("danny", danny);
            data.put("smith", smith);
        }
        return data;
    }
    
    private Collection<GrantedAuthority> getGrants(String role) {
        return AuthorityUtils.commaSeparatedStringToAuthorityList(role);
    }
}

UserService.java

杩欓噷鍐嶆ā鎷熶竴涓?service锛屼富瑕佸氨鏄ā浠挎暟鎹簱鐨勬搷浣溿€?/p>

@Service
public class UserService {

    @Autowired
    private Database database;

    public UserEntity getUserByUsername(String username) {
        return database.getDatabase().get(username);
    }
}

JwtUtil.java

鑷繁缂栧啓鐨勪竴涓伐鍏风被锛屼富瑕佽礋璐?JWT 鐨勭鍚嶅拰閴存潈銆?/p>

public class JwtUtil {

    // 杩囨湡鏃堕棿5鍒嗛挓
    private final static long EXPIRE_TIME = 5 * 60 * 1000;

    /**
     * 鐢熸垚绛惧悕,5min鍚庤繃鏈?
     * @param username 鐢ㄦ埛鍚?
     * @param secret 鐢ㄦ埛鐨勫瘑鐮?
     * @return 鍔犲瘑鐨則oken
     */
    public static String sign(String username, String secret) {
        Date expireDate = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            return JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(expireDate)
                    .sign(algorithm);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 鏍¢獙token鏄惁姝g‘
     * @param token 瀵嗛挜
     * @param secret 鐢ㄦ埛鐨勫瘑鐮?
     * @return 鏄惁姝g‘
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 鑾峰緱token涓殑淇℃伅鏃犻渶secret瑙e瘑涔熻兘鑾峰緱
     * @return token涓寘鍚殑鐢ㄦ埛鍚?
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }
}

Spring Security 鏀归€?/h2>

鐧诲叆杩欏潡锛屾垜浠娇鐢ㄨ嚜瀹氫箟鐨?JwtAuthenticationFilter 鏉ヨ繘琛岀櫥鍏ャ€?/p>

璇锋眰閴存潈锛屾垜浠娇鐢ㄨ嚜瀹氫箟鐨?JwtAuthorizationFilter 鏉ュ鐞嗐€?/p>

涔熻澶у瑙夊緱涓や釜鍗曡瘝闀跨殑鏈夌偣鍍忥紝馃槣銆?/p>

UserDetailsServiceImpl.java

鎴戜滑棣栧厛瀹炵幇瀹樻柟鐨?UserDetailsService 鎺ュ彛锛岃繖閲屼富瑕佽礋璐d竴涓粠鏁版嵁搴撴嬁鏁版嵁鐨勬搷浣溿€?/p>

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserEntity userEntity = userService.getUserByUsername(username);
        if (userEntity == null) {
            throw new UsernameNotFoundException("This username didn't exist.");
        }
        return new User(userEntity.getUsername(), userEntity.getPassword(), userEntity.getRole());
    }
}

鍚庡簭鎴戜滑杩橀渶瑕佸鍏惰繘琛岀紦瀛樻敼閫狅紝涓嶇劧姣忔璇锋眰閮借浠庢暟鎹簱鎷夸竴娆℃暟鎹壌鏉冿紝瀵规暟鎹簱鍘嬪姏澶ぇ浜嗐€?/p>

JwtAuthenticationFilter.java

杩欎釜杩囨护鍣ㄤ富瑕佸鐞嗙櫥鍏ユ搷浣滐紝鎴戜滑缁ф壙浜?UsernamePasswordAuthenticationFilter锛岃繖鏍疯兘澶уぇ绠€鍖栨垜浠殑宸ヤ綔閲忋€?/p>

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    /*
    杩囨护鍣ㄤ竴瀹氳璁剧疆 AuthenticationManager锛屾墍浠ユ澶勬垜浠繖涔堢紪鍐欙紝杩欓噷鐨?AuthenticationManager
    鎴戜細浠?Security 閰嶇疆鐨勬椂鍊欎紶鍏?
    */
    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        /*
        杩愯鐖剁被 UsernamePasswordAuthenticationFilter 鐨勬瀯閫犳柟娉曪紝鑳藉璁剧疆姝ゆ护鍣ㄦ寚瀹?
        鏂规硶涓?POST [\login]
        */
        super();
        setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        // 浠庤姹傜殑 POST 涓嬁鍙?username 鍜?password 涓や釜瀛楁杩涜鐧诲叆
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
        // 璁剧疆涓€浜涘鎴?IP 鍟ヤ俊鎭紝鍚庨潰鎯崇敤鐨勮瘽鍙互鐢紝铏界劧娌″暐鐢?
        setDetails(request, token);
        // 浜ょ粰 AuthenticationManager 杩涜閴存潈
        return getAuthenticationManager().authenticate(token);
    }

    /*
    閴存潈鎴愬姛杩涜鐨勬搷浣滐紝鎴戜滑杩欓噷璁剧疆杩斿洖鍔犲瘑鍚庣殑 token
    */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        handleResponse(request, response, authResult, null);
    }

    /*
    閴存潈澶辫触杩涜鐨勬搷浣滐紝鎴戜滑杩欓噷灏辫繑鍥?鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒 鐨勪俊鎭?
    */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        handleResponse(request, response, null, failed);
    }

    private void handleResponse(HttpServletRequest request, HttpServletResponse response, Authentication authResult, AuthenticationException failed) throws IOException, ServletException {
        ObjectMapper mapper = new ObjectMapper();
        ResponseEntity responseEntity = new ResponseEntity();
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        if (authResult != null) {
            // 澶勭悊鐧诲叆鎴愬姛璇锋眰
            User user = (User) authResult.getPrincipal();
            String token = JwtUtil.sign(user.getUsername(), user.getPassword());
            responseEntity.setStatus(HttpStatus.OK.value());
            responseEntity.setMsg("鐧诲叆鎴愬姛");
            responseEntity.setData("Bearer " + token);
            response.setStatus(HttpStatus.OK.value());
            response.getWriter().write(mapper.writeValueAsString(responseEntity));
        } else {
            // 澶勭悊鐧诲叆澶辫触璇锋眰
            responseEntity.setStatus(HttpStatus.BAD_REQUEST.value());
            responseEntity.setMsg("鐢ㄦ埛鍚嶆垨瀵嗙爜閿欒");
            responseEntity.setData(null);
            response.setStatus(HttpStatus.BAD_REQUEST.value());
            response.getWriter().write(mapper.writeValueAsString(responseEntity));
        }
    }
}

private void handleResponse() 姝ゅ澶勭悊鐨勬柟娉曚笉鏄緢濂斤紝鎴戠殑鎯虫硶鏄烦杞埌鎺у埗鍣ㄤ腑杩涜澶勭悊锛屼絾鏄繖鏍烽壌鏉冩垚鍔熺殑 token 甯︿笉杩囧幓锛屾墍浠ュ厛杩欎箞鍐欎簡锛屾湁鐐瑰鏉傘€?/p>

JwtAuthorizationFilter.java

杩欎釜杩囨护鍣ㄥ鐞嗘瘡涓姹傞壌鏉冿紝鎴戜滑閫夋嫨缁ф壙 BasicAuthenticationFilter 锛岃€冭檻鍒?Basic 璁よ瘉鍜?JWT 姣旇緝鍍忥紝灏遍€夋嫨浜嗗畠銆?/p>

public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    private UserDetailsService userDetailsService;

    // 浼氫粠 Spring Security 閰嶇疆鏂囦欢閭i噷浼犺繃鏉?
    public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserDetailsService userDetailsService) {
        super(authenticationManager);
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 鍒ゆ柇鏄惁鏈?token锛屽苟涓旇繘琛岃璇?
        Authentication token = getAuthentication(request);
        if (token == null) {
            chain.doFilter(request, response);
            return;
        }
        // 璁よ瘉鎴愬姛
        SecurityContextHolder.getContext().setAuthentication(token);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        if (header == null || ! header.startsWith("Bearer ")) {
            return null;
        }

        String token = header.split(" ")[1];
        String username = JwtUtil.getUsername(token);
        UserDetails userDetails = null;
        try {
            userDetails = userDetailsService.loadUserByUsername(username);
        } catch (UsernameNotFoundException e) {
            return null;
        }
        if (! JwtUtil.verify(token, username, userDetails.getPassword())) {
            return null;
        }
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
}

SecurityConfiguration.java

姝ゅ鎴戜滑杩涜 Security 鐨勯厤缃紝骞朵笖瀹炵幇缂撳瓨鍔熻兘銆傜紦瀛樿繖鍧楁垜浠娇鐢ㄥ畼鏂圭幇鎴愮殑 CachingUserDetailsService 锛屽敮鐙殑缂虹偣灏辨槸瀹冩病鏈?public 鏂规硶锛屾垜浠笉鑳芥甯稿疄渚嬪寲锛岄渶瑕佹洸绾挎晳鍥斤紝涓嬮潰浠g爜涔熸湁璇︾粏璇存槑銆?/p>

// 寮€鍚?Security
@EnableWebSecurity
// 寮€鍚敞瑙i厤缃敮鎸?
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsServiceImpl;

    // Spring Boot 鐨?CacheManager锛岃繖閲屾垜浠娇鐢?JCache
    @Autowired
    private CacheManager cacheManager;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 寮€鍚法鍩?
        http.cors()
                .and()
                // security 榛樿 csrf 鏄紑鍚殑锛屾垜浠娇鐢ㄤ簡 token 锛岃繖涓篃娌℃湁浠€涔堝繀瑕佷簡
                .csrf().disable()
                .authorizeRequests()
                // 榛樿鎵€鏈夎姹傞€氳繃锛屼絾鏄垜浠鍦ㄩ渶瑕佹潈闄愮殑鏂规硶鍔犱笂瀹夊叏娉ㄨВ锛岃繖鏍锋瘮鍐欐閰嶇疆鐏垫椿寰堝
                .anyRequest().permitAll()
                .and()
                // 娣诲姞鑷繁缂栧啓鐨勪袱涓繃婊ゅ櫒
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager(), cachingUserDetailsService(userDetailsServiceImpl)))
                // 鍓嶅悗绔垎绂绘槸 STATELESS锛屾晠 session 浣跨敤璇ョ瓥鐣?
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    // 姝ゅ閰嶇疆 AuthenticationManager锛屽苟涓斿疄鐜扮紦瀛?
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 瀵硅嚜宸辩紪鍐欑殑 UserDetailsServiceImpl 杩涗竴姝ュ寘瑁咃紝瀹炵幇缂撳瓨
        CachingUserDetailsService cachingUserDetailsService = cachingUserDetailsService(userDetailsServiceImpl);
        // jwt-cache 鎴戜滑鍦?ehcache.xml 閰嶇疆鏂囦欢涓湁澹版槑
        UserCache userCache = new SpringCacheBasedUserCache(cacheManager.getCache("jwt-cache"));
        cachingUserDetailsService.setUserCache(userCache);
        /*
        security 榛樿閴存潈瀹屾垚鍚庝細鎶婂瘑鐮佹姽闄わ紝浣嗘槸杩欓噷鎴戜滑浣跨敤鐢ㄦ埛鐨勫瘑鐮佹潵浣滀负 JWT 鐨勭敓鎴愬瘑閽ワ紝
        濡傛灉琚姽闄や簡锛屽湪瀵?JWT 杩涜绛惧悕鐨勬椂鍊欏氨鎷夸笉鍒扮敤鎴峰瘑鐮佷簡锛屾晠姝ゅ鍏抽棴浜嗚嚜鍔ㄦ姽闄ゅ瘑鐮併€?
         */
        auth.eraseCredentials(false);
        auth.userDetailsService(cachingUserDetailsService);
    }

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

    /*
    姝ゅ鎴戜滑瀹炵幇缂撳瓨鐨勬椂鍊欙紝鎴戜滑浣跨敤浜嗗畼鏂圭幇鎴愮殑 CachingUserDetailsService 锛屼絾鏄繖涓被鐨勬瀯閫犳柟娉曚笉鏄?public 鐨勶紝
    鎴戜滑涓嶈兘澶熸甯稿疄渚嬪寲锛屾墍浠ュ湪杩欓噷杩涜鏇茬嚎鏁戝浗銆?
     */
    private CachingUserDetailsService cachingUserDetailsService(UserDetailsServiceImpl delegate) {

        Constructor<CachingUserDetailsService> ctor = null;
        try {
            ctor = CachingUserDetailsService.class.getDeclaredConstructor(UserDetailsService.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        Assert.notNull(ctor, "CachingUserDetailsService constructor is null");
        ctor.setAccessible(true);
        return BeanUtils.instantiateClass(ctor, delegate);
    }
}

Ehcache 閰嶇疆

Ehcache 3 寮€濮嬶紝缁熶竴浣跨敤浜?JCache锛屽氨鏄?JSR107 鏍囧噯锛岀綉涓婂緢澶氭暀绋嬮兘鏄熀浜?Ehcache 2 鐨勶紝鎵€浠ュぇ瀹跺彲鑳藉湪鍙傜収缃戜笂鐨勬暀绋嬩細閬囧埌寰堝鍧戙€?/p>

JSR107锛歟mm锛屽叾瀹?JSR107 鏄竴绉嶇紦瀛樻爣鍑嗭紝鍚勪釜妗嗘灦鍙閬靛畧杩欎釜鏍囧噯锛屽氨鏄幇瀹炲ぇ涓€缁熴€傚樊涓嶅灏辨槸鎴戜笉闇€瑕佹洿鏀圭郴缁熶唬鐮侊紝涔熻兘闅忔剰鏇存崲搴曞眰鐨勭紦瀛樼郴缁熴€?/p>

鍦?resources 鐩綍涓嬪垱寤?ehcache.xml 鏂囦欢锛?/p>

<ehcache:config
        xmlns:ehcache="http://www.ehcache.org/v3"
        xmlns:jcache="http://www.ehcache.org/v3/jsr107">

    <ehcache:cache alias="jwt-cache">
        <!-- 鎴戜滑浣跨敤鐢ㄦ埛鍚嶄綔涓虹紦瀛樼殑 key锛屾晠浣跨敤 String -->
        <ehcache:key-type>java.lang.String</ehcache:key-type>
        <ehcache:value-type>org.springframework.security.core.userdetails.User</ehcache:value-type>
        <ehcache:expiry>
            <ehcache:ttl unit="days">1</ehcache:ttl>
        </ehcache:expiry>
        <!-- 缂撳瓨瀹炰綋鐨勬暟閲?-->
        <ehcache:heap unit="entries">2000</ehcache:heap>
    </ehcache:cache>

</ehcache:config>

鍦?application.properties 涓紑鍚紦瀛樻敮鎸侊細

spring.cache.type=jcache
spring.cache.jcache.config=classpath:ehcache.xml

缁熶竴鍏ㄥ眬寮傚父

鎴戜滑瑕佹妸寮傚父鐨勮繑鍥炲舰寮忎篃缁熶竴浜嗭紝杩欐牱鎵嶈兘鏂逛究鍓嶇鐨勮皟鐢ㄣ€?/p>

鎴戜滑骞冲父浼氫娇鐢?@RestControllerAdvice 鏉ョ粺涓€寮傚父锛屼絾鏄畠鍙兘绠$悊 Controller 灞傞潰鎶涘嚭鐨勫紓甯搞€係ecurity 涓姏鍑虹殑寮傚父涓嶄細鎶佃揪 Controller锛屾棤娉曡 @RestControllerAdvice 鎹曡幏锛屾晠鎴戜滑杩樿鏀归€?ErrorController 銆?/p>

@RestController
public class CustomErrorController implements ErrorController {

    @Override
    public String getErrorPath() {
        return "/error";
    }

    @RequestMapping("/error")
    public ResponseEntity handleError(HttpServletRequest request, HttpServletResponse response) {
        return new ResponseEntity(response.getStatus(), (String) request.getAttribute("javax.servlet.error.message"), null);
    }
}

娴嬭瘯

鍐欎釜鎺у埗鍣ㄨ瘯璇曪紝澶у涔熷彲浠ュ弬鑰冩垜鎺у埗鍣ㄩ噷闈㈣幏鍙栫敤鎴蜂俊鎭殑鏂瑰紡锛屾帹鑽愪娇鐢?@AuthenticationPrincipal 杩欎釜娉ㄨВ锛侊紒锛?/p>

@RestController
public class MainController {

    // 浠讳綍浜洪兘鍙互璁块棶锛屽湪鏂规硶涓垽鏂敤鎴锋槸鍚﹀悎娉?
    @GetMapping("everyone")
    public ResponseEntity everyone() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (! (authentication instanceof AnonymousAuthenticationToken)) {
            // 鐧诲叆鐢ㄦ埛
            return new ResponseEntity(HttpStatus.OK.value(), "You are already login", authentication.getPrincipal());
        } else {
            return new ResponseEntity(HttpStatus.OK.value(), "You are anonymous", null);
        }
    }

    @GetMapping("user")
    @PreAuthorize("hasAuthority('ROLE_USER')")
    public ResponseEntity user(@AuthenticationPrincipal UsernamePasswordAuthenticationToken token) {
        return new ResponseEntity(HttpStatus.OK.value(), "You are user", token);
    }

    @GetMapping("admin")
    @IsAdmin
    public ResponseEntity admin(@AuthenticationPrincipal UsernamePasswordAuthenticationToken token) {
        return new ResponseEntity(HttpStatus.OK.value(), "You are admin", token);
    }
}

鎴戣繖閲岃繕浣跨敤浜?@IsAdmin 娉ㄨВ锛?code>@IsAdmin 娉ㄨВ濡備笅锛?/p>

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
public @interface IsAdmin {
}

杩欐牱鑳界渷鍘绘瘡娆$紪鍐欎竴闀夸覆鐨?@PreAuthorize() 锛岃€屼笖鏇村姞鐩磋銆?/p>

FAQ

濡備綍瑙e喅JWT杩囨湡闂锛?/h3>

鎴戜滑鍙互鍦?JwtAuthorizationFilter 涓姞鐐规枡锛屽鏋滅敤鎴峰揩杩囨湡浜嗭紝杩斿洖涓壒鍒殑鐘舵€佺爜锛屽墠绔敹鍒版鐘舵€佺爜鍘昏闂?GET /re_authentication 鎼哄甫鑰佺殑 token 閲嶆柊鎷夸竴涓柊鐨?token 鍗冲彲銆?/p>

濡備綍浣滃簾宸查鍙戞湭杩囨湡鐨?token锛?/h3>

鎴戜釜浜虹殑鎯虫硶鏄妸姣忔鐢熸垚鐨?token 鏀惧叆缂撳瓨涓紝姣忔璇锋眰閮戒粠缂撳瓨閲屾嬁锛屽鏋滄病鏈夊垯浠h〃姝ょ紦瀛樻姤搴熴€?/p>

椤圭洰鍦板潃锛歨ttps://github.com/Smith-Cruise/Spring-Boot-Security-JWT-SPA

鏈枃棣栧彂浜庡叕浼楀彿锛欽ava鐗坵eb椤圭洰锛屾杩庡叧娉ㄨ幏鍙栨洿澶氱簿褰╁唴瀹?/p>


https://www.xamrdz.com/backend/3vm1945680.html

相关文章: