轉載至 原文地址java
本教程主要詳細講解SpringBoot Security整合JWT受權RestAPI。git
技術 | 版本 |
---|---|
Java | 1.8+ |
SpringBoot | 2.x.x |
Security | 5.x |
JWT | 0.9.0 |
mvn archetype:generate -DgroupId=com.edurt.sli.slisj -DartifactId=spring-learn-integration-security-jwt -DarchetypeArtifactId=maven-archetype-quickstart -Dversion=1.0.0 -DinteractiveMode=false
<?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"> <parent> <artifactId>spring-learn-integration-security</artifactId> <groupId>com.edurt.sli</groupId> <version>1.0.0</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-learn-integration-security-jwt</artifactId> <name>SpringBoot Security整合JWT受權RestAPI</name> <properties> <system.java.version>1.8</system.java.version> <plugin.maven.compiler.version>3.3</plugin.maven.compiler.version> <dependency.springboot2.common.version>2.0.3.RELEASE</dependency.springboot2.common.version> <dependency.lombok.version>1.18.6</dependency.lombok.version> <dependency.jwt.version>0.9.0</dependency.jwt.version> <dependency.jackson.version>2.9.9</dependency.jackson.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>${dependency.springboot2.common.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> <version>${dependency.springboot2.common.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>${dependency.jwt.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${dependency.lombok.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${dependency.jackson.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${dependency.springboot2.common.version}</version> <configuration> <fork>true</fork> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${plugin.maven.compiler.version}</version> <configuration> <source>${system.java.version}</source> <target>${system.java.version}</target> </configuration> </plugin> </plugins> </build> </project>
spring-boot-starter-security
啓動spring security安全框架jjwt
啓動spring security jwt框架支持github
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Component; /** * <p> SpringBootSecurityJwtIntegration </p> * <p> Description : SpringBootSecurityJwtIntegration </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:45 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @SpringBootApplication @Component(value = "com.edurt.sli.slisj") public class SpringBootSecurityJwtIntegration { public static void main(String[] args) { SpringApplication.run(SpringBootSecurityJwtIntegration.class, args); } }
/src/main/java/com/edurt/sli/slisj
目錄下建立config目錄,並在該目錄下新建jwt目錄,在該目錄下新建JwtTokenTemplate
工具模板/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.config.jwt; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.io.Serializable; import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * <p> JwtTokenTemplate </p> * <p> Description : JwtTokenTemplate </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:49 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @Component public class JwtTokenTemplate implements Serializable { private static final String CLAIM_KEY_USERNAME = "sub"; private static final long EXPIRATION_TIME = 432000000; private static final String SECRET = "secret"; public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(16); claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); return Jwts.builder() .setClaims(claims) .setExpiration(new Date(Instant.now().toEpochMilli() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { User user = (User) userDetails; String username = getUsernameFromToken(token); return (username.equals(user.getUsername()) && !isTokenExpired(token)); } public Boolean isTokenExpired(String token) { Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } public String getUsernameFromToken(String token) { String username = getClaimsFromToken(token).getSubject(); return username; } public Date getExpirationDateFromToken(String token) { Date expiration = getClaimsFromToken(token).getExpiration(); return expiration; } private Claims getClaimsFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token) .getBody(); return claims; } }
jwt
該目錄下新建JwtTokenFilter
過濾器/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.config.jwt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * <p> JwtTokenFilter </p> * <p> Description : JwtTokenFilter </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:49 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @Component public class JwtTokenFilter extends OncePerRequestFilter { public static final String HEADER_STRING = "Authorization"; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenTemplate jwtTokenTemplate; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String token = request.getHeader(HEADER_STRING); if (null != token) { String username = jwtTokenTemplate.getUsernameFromToken(token); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtTokenTemplate.validateToken(token, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails( request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } chain.doFilter(request, response); } }
config
目錄下新建JwtSecurityConfig文件/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.config; import com.edurt.sli.slisj.config.jwt.JwtTokenFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; /** * <p> JwtSecurityConfig </p> * <p> Description : JwtSecurityConfig </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:46 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @Configuration @EnableWebSecurity public class JwtSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() .antMatchers("/auth/login").permitAll() .anyRequest().authenticated(); http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); http.headers().cacheControl(); } @Bean public JwtTokenFilter authenticationTokenFilterBean() { return new JwtTokenFilter(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
config
目錄下新建JwtUserDetailsService文件/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.config; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.ArrayList; /** * <p> JwtUserDetailsService </p> * <p> Description : JwtUserDetailsService </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:54 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @Service public class JwtUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { if (userName.equals("admin")) { return new User("admin", "$2a$10$slYQmyNdGzTn7ZLBXBChFOC9f6kFjAqPhccnP6DxlWXx2lPk1C3G6", new ArrayList<>()); } return null; } }
server.port=8989
在/src/main/java/com/edurt/sli/slisj
目錄下建立param目錄,並在該目錄下新建JwtParam文件web
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.param; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; /** * <p> JwtParam </p> * <p> Description : JwtParam </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:59 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @Data @ToString @AllArgsConstructor @NoArgsConstructor public class JwtParam { private String username; private String password; }
在/src/main/java/com/edurt/sli/slisj
目錄下建立controller目錄,並在該目錄下新建HelloJwtController文件spring
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.edurt.sli.slisj.controller; import com.edurt.sli.slisj.config.JwtUserDetailsService; import com.edurt.sli.slisj.config.jwt.JwtTokenTemplate; import com.edurt.sli.slisj.param.JwtParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.*; /** * <p> HelloJwtController </p> * <p> Description : HelloJwtController </p> * <p> Author : qianmoQ </p> * <p> Version : 1.0 </p> * <p> Create Time : 2019-11-26 20:58 </p> * <p> Author Email: <a href="mailTo:shichengoooo@163.com">qianmoQ</a> </p> */ @RestController @RequestMapping(value = "auth") public class HelloJwtController { @Autowired private JwtTokenTemplate jwtTokenTemplate; @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUserDetailsService userDetailsService; @PostMapping(value = "login") public String login(@RequestBody JwtParam body) throws AuthenticationException { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(body.getUsername(), body.getPassword()); Authentication authentication = authenticationManager.authenticate(authenticationToken); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = userDetailsService.loadUserByUsername(body.getUsername()); return jwtTokenTemplate.generateToken(userDetails); } @GetMapping(value = "hello") public String hello() { return "Hello Jwt!!!"; } }
curl -X GET 'http://localhost:8989/auth/hello'
會出現如下錯誤信息express
{ "timestamp": "2019-11-26T13:05:05.204+0000", "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "/auth/hello" }
/auth/login
去得到受權的tokencurl -X POST 'http://127.0.0.1:8989/auth/login' --header 'Content-Type: application/json' -d '{"username": "admin", "password": "password"}'
返回如下token信息apache
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU3NTIwNTg4OH0.zc3JTsIHIZSmi-hrgCB1AKrrjVWtnWB4YJjOhzml2k9qRdTGdoDYKM1XriQIAInvIrTDDkpozT4Ny58Wcpm4JA
/auth/hello
接口獲取數據curl -X GET 'http://127.0.0.1:8989/auth/hello' --header 'Authorization: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU3NTIwNTg4OH0.zc3JTsIHIZSmi-hrgCB1AKrrjVWtnWB4YJjOhzml2k9qRdTGdoDYKM1XriQIAInvIrTDDkpozT4Ny58Wcpm4JA'
返回如下信息json
Hello Jwt!!!
此時咱們已經完成JWT受權配置安全
mvn clean package -Dmaven.test.skip=true -X
運行打包後的文件便可springboot
java -jar target/spring-learn-integration-security-jwt-1.0.0.jar