brief introduction
JSON Web Token (JWT) is the most popular cross domain authentication solution. Introduction to JSON Web Token - Ruan Yifeng , this article can help you understand the concept of jwt. This paper focuses on Spring Boot combined with jwt to realize the safe calling of interface in front and back end separation.
Quick start
The previous article has explained Spring Security. In this section, the configuration related to Spring Security is not explained in detail. If you don't know Spring Security, move to Detailed explanation of Spring Boot Security.
Building tables
DROP TABLE IF EXISTS `user`; DROP TABLE IF EXISTS `role`; DROP TABLE IF EXISTS `user_role`; DROP TABLE IF EXISTS `role_permission`; DROP TABLE IF EXISTS `permission`; CREATE TABLE `user` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `role` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `user_role` ( `user_id` bigint(11) NOT NULL, `role_id` bigint(11) NOT NULL ); CREATE TABLE `role_permission` ( `role_id` bigint(11) NOT NULL, `permission_id` bigint(11) NOT NULL ); CREATE TABLE `permission` ( `id` bigint(11) NOT NULL AUTO_INCREMENT, `url` varchar(255) NOT NULL, `name` varchar(255) NOT NULL, `description` varchar(255) NULL, `pid` bigint(11) NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO user (id, username, password) VALUES (1,'user','e10adc3949ba59abbe56e057f20f883e'); INSERT INTO user (id, username , password) VALUES (2,'admin','e10adc3949ba59abbe56e057f20f883e'); INSERT INTO role (id, name) VALUES (1,'USER'); INSERT INTO role (id, name) VALUES (2,'ADMIN'); INSERT INTO permission (id, url, name, pid) VALUES (1,'/user/hi','',0); INSERT INTO permission (id, url, name, pid) VALUES (2,'/admin/hi','',0); INSERT INTO user_role (user_id, role_id) VALUES (1, 1); INSERT INTO user_role (user_id, role_id) VALUES (2, 1); INSERT INTO user_role (user_id, role_id) VALUES (2, 2); INSERT INTO role_permission (role_id, permission_id) VALUES (1, 1); INSERT INTO role_permission (role_id, permission_id) VALUES (2, 1); INSERT INTO role_permission (role_id, permission_id) VALUES (2, 2);
Project structure
resources |___application.yml java |___com | |____gf | | |____SpringbootJwtApplication.java | | |____config | | | |____.DS_Store | | | |____SecurityConfig.java | | | |____MyFilterSecurityInterceptor.java | | | |____MyInvocationSecurityMetadataSourceService.java | | | |____MyAccessDecisionManager.java | | |____entity | | | |____User.java | | | |____RolePermisson.java | | | |____Role.java | | |____mapper | | | |____PermissionMapper.java | | | |____UserMapper.java | | | |____RoleMapper.java | | |____utils | | | |____JwtTokenUtil.java | | |____controller | | | |____AuthController.java | | |____filter | | | |____JwtTokenFilter.java | | |____service | | | |____impl | | | | |____AuthServiceImpl.java | | | | |____UserDetailsServiceImpl.java | | | |____AuthService.java
critical code
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.0.0</version> </dependency>
application.yml
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-security-jwt?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: root
SecurityConfig
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { //Check user auth.userDetailsService( userDetailsService ).passwordEncoder( new PasswordEncoder() { //Encrypt password @Override public String encode(CharSequence charSequence) { System.out.println(charSequence.toString()); return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()); } //Judge and match the password @Override public boolean matches(CharSequence charSequence, String s) { String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()); boolean res = s.equals( encode ); return res; } } ); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() //Because JWT is used, HttpSession is not required .sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS).and() .authorizeRequests() //OPTIONS request full release .antMatchers( HttpMethod.OPTIONS, "/**").permitAll() //Login interface release .antMatchers("/auth/login").permitAll() //All other interfaces are verified .anyRequest().authenticated(); //Use the custom Token filter to verify whether the requested Token is legal http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class); http.headers().cacheControl(); } @Bean public JwtTokenFilter authenticationTokenFilterBean() throws Exception { return new JwtTokenFilter(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
JwtTokenUtil
/** * JWT Tool class */ @Component public class JwtTokenUtil implements Serializable { private static final String CLAIM_KEY_USERNAME = "sub"; /** * 5 Days (milliseconds) */ private static final long EXPIRATION_TIME = 432000000; /** * JWT Password */ private static final String SECRET = "secret"; /** * Issue JWT */ 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(); } /** * Verify JWT */ public Boolean validateToken(String token, UserDetails userDetails) { User user = (User) userDetails; String username = getUsernameFromToken( token ); return (username.equals( user.getUsername() ) && !isTokenExpired( token )); } /** * Get whether the token expires */ public Boolean isTokenExpired(String token) { Date expiration = getExpirationDateFromToken( token ); return expiration.before( new Date() ); } /** * Get username according to token */ public String getUsernameFromToken(String token) { String username = getClaimsFromToken( token ).getSubject(); return username; } /** * Get the expiration time of token */ public Date getExpirationDateFromToken(String token) { Date expiration = getClaimsFromToken( token ).getExpiration(); return expiration; } /** * Parsing JWT */ private Claims getClaimsFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey( SECRET ) .parseClaimsJws( token ) .getBody(); return claims; } }
JwtTokenFilter
@Component public class JwtTokenFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; /** * Header Key where Token is stored */ public static final String HEADER_STRING = "Authorization"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String token = request.getHeader( HEADER_STRING ); if (null != token) { String username = jwtTokenUtil.getUsernameFromToken(token); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.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); } }
AuthServiceImpl
@Service public class AuthServiceImpl implements AuthService { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override public String login(String username, String password) { UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken( username, password ); Authentication authentication = authenticationManager.authenticate(upToken); SecurityContextHolder.getContext().setAuthentication(authentication); UserDetails userDetails = userDetailsService.loadUserByUsername( username ); String token = jwtTokenUtil.generateToken(userDetails); return token; } }
The key code is these. Other class codes refer to the source address provided later.
Verification
Login, get token
curl -X POST -d "username=admin&password=123456" http://127.0.0.1:8080/auth/login
Return
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1NDQ1MzUwMX0.sglVeqnDGUL9pH1oP3Lh9XrdzJIS42VKBApd2nPJt7e1TKhCEY7AUfIXnzG9vc885_jTq4-h8R6YCtRRJzl8fQ
Access resources without token
curl -X POST -d "name=zhangsan" http://127.0.0.1:8080/admin/hi
Back, access denied
{ "timestamp": "2019-03-31T08:50:55.894+0000", "status": 403, "error": "Forbidden", "message": "Access Denied", "path": "/auth/login" }
Carry token to access resources
curl -X POST -H "Authorization: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTU1NDQ1MzUwMX0.sglVeqnDGUL9pH1oP3Lh9XrdzJIS42VKBApd2nPJt7e1TKhCEY7AUfIXnzG9vc885_jTq4-h8R6YCtRRJzl8fQ" -d "name=zhangsan" http://127.0.0.1:8080/admin/hi
Return right
hi zhangsan , you have 'admin' role
Source code
https://github.com/gf-huanchupk/SpringBootLearning/tree/master/springboot-jwt
Welcome to scan code or WeChat search public number "SpringCloud micro service development" pay close attention to me, pay attention to surprises ~