前言 关于系统最终想实现的功能:使用token来实现登录校验,用户登录后拿到token,然后将token放入httpHeader,之后每次接口请求都携带token,验证成功才可进行正常访问流程
储备知识
token与 jwt (JSON Web Token)介绍 JWT 的原理 JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
1 2 3 4 5 { "姓名" : "张三" , "角色" : "管理员" , "到期时间" : "2018年7月1日0点0分" }
JWT 的数据结构 它是一个很长的字符串,中间用点(.
)分隔成三个部分。注意,JWT 内部是没有换行的,这里只是为了便于展示,将它写成了几行。
JWT 的三个部分依次如下。
1 2 3 4 5 6 7 8 9 10 11 12 { "code" : 200 , "data" : { "token" : "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjcWMiLCJjcmVhdGVkIjoxNjk0Njc1MjM2ODE2LCJleHAiOjIxMjY2NzUyMzZ9.Fi1Th2VOXpTASN3sRZmuvy4rNsGtFkaxIrkx85-I29PVYnOfxTc_jl2XwnrELiWCP4tHxnrUqUjkHyoHxrPNRA" } , "msg" : "小朋友,登陆成功!" } eyJhbGciOiJIUzUxMiJ9. eyJzdWIiOiJjcWMiLCJjcmVhdGVkIjoxNjk0Njc1MjM2ODE2LCJleHAiOjIxMjY2NzUyMzZ9. Fi1Th2VOXpTASN3sRZmuvy4rNsGtFkaxIrkx85-I29PVYnOfxTc_jl2XwnrELiWCP4tHxnrUqUjkHyoHxrPNRA
Header(头部)
Payload(负载)
Signature(签名)
Header 部分是一个 JSON 对象,描述 JWT 的元数据,通常是下面的样子。
1 2 3 4 { "alg" : "HS256" , "typ" : "JWT" }
上面代码中,alg
属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ
属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
。
最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串。
Payload Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。
这个 JSON 对象也要使用 Base64URL 算法转成字符串。
Signature Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
1 HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用”点”(.
)分隔,就可以返回给用户。
JWT 工具类 有很多常见的工具类,我这边用的是这个
1 2 3 4 5 <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt</artifactId > <version > 0.9.1</version > </dependency >
spring security简介 介绍的文章一抓一大把,这边主要说一下他的几个核心东西
用户认证(Authentication) 是验证您的身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。
用户授权(Authorization) 发生在 **Authentication(认证)**之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。
过滤器链
核心组件
AuthenticationManager
SecurityContextHolder
PasswordEncoder
UserDetails
UserDetailsService
BasicAuthenticationFilter
AuthenticationEntryPoint
登录流程图
集成流程(代码部分,不看可以跳过) gitee地址: https://gitee.com/cactuscode/springboot-spring-security-jwt.git
集成spring security 数据库 需要建两个表
user
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0 ;DROP TABLE IF EXISTS `user `;CREATE TABLE `user ` ( `uid` int (11 ) NOT NULL AUTO_INCREMENT, `username` varchar (255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL , `password` varchar (255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL , `role_id` int (11 ) NULL DEFAULT NULL , PRIMARY KEY (`uid`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic ;INSERT INTO `user ` VALUES (1 , 'cqc' , '$2a$10$jOpeMlGJ7ZRE.8mebeVQ4uPDS89wdBmLTmqXcfmw8ncRzsCjY7KlC' , 2 );SET FOREIGN_KEY_CHECKS = 1 ;
role
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0 ;DROP TABLE IF EXISTS `role`;CREATE TABLE `role` ( `role_id` int (11 ) NOT NULL , `role_name` varchar (255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL , `description` varchar (255 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL , PRIMARY KEY (`role_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic ;INSERT INTO `role` VALUES (1 , 'ROLE_ADMIN' , 'ROLE_ADMIN' );INSERT INTO `role` VALUES (2 , 'ROLE_NORMAL' , 'ROLE_NORMAL' );SET FOREIGN_KEY_CHECKS = 1 ;
maven依赖 springboot版本 2.7.5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 <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 > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-api</artifactId > <version > 0.11.5</version > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-impl</artifactId > <version > 0.11.5</version > </dependency > <dependency > <groupId > io.jsonwebtoken</groupId > <artifactId > jjwt-jackson</artifactId > <version > 0.11.5</version > </dependency > <dependency > <groupId > com.baomidou</groupId > <artifactId > mybatis-plus-boot-starter</artifactId > <version > 3.5.3.2</version > </dependency > <dependency > <groupId > cn.hutool</groupId > <artifactId > hutool-all</artifactId > <version > 5.8.21</version > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-devtools</artifactId > <scope > runtime</scope > <optional > true</optional > </dependency > <dependency > <groupId > com.mysql</groupId > <artifactId > mysql-connector-j</artifactId > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <optional > true</optional > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-test</artifactId > <scope > test</scope > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > org.springframework.security</groupId > <artifactId > spring-security-test</artifactId > <scope > test</scope > </dependency >
application.yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 server: port: 8001 spring: datasource: url: jdbc:mysql://192.168.55.101:3306/cqc?characterEncoding=utf-8&autoReconnect=true&useSSL=false username: root password: Gmmysql_300098 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: mapper-locations: classpath:mapper/*.xml logging: level: com.chang.springsecurity.mapper: debug
User 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package com.cactus.springsecurity.entity;import com.fasterxml.jackson.annotation.JsonIgnore;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;import java.util.Collection;import java.util.List;@AllArgsConstructor @NoArgsConstructor @Data public class User implements UserDetails { private String uid; private String username; private String password; private List<Role> roles; @JsonIgnore @Override public Collection<? extends GrantedAuthority > getAuthorities() { List<GrantedAuthority> authorities = new ArrayList <>(); if (roles == null ) { return authorities; } for (Role role : roles) { authorities.add(new SimpleGrantedAuthority (role.getRoleName())); } return authorities; } @Override public boolean isAccountNonExpired () { return true ; } @Override public boolean isAccountNonLocked () { return true ; } @Override public boolean isCredentialsNonExpired () { return true ; } @Override public boolean isEnabled () { return true ; } }
Role 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.cactus.springsecurity.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public class Role { private Long id; private String roleName; private String description; }
Result 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.cactus.springsecurity.entity.response;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @NoArgsConstructor @AllArgsConstructor public class Result { private int code; private Object data; private String msg; }
UserMapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.cactus.springsecurity.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.cactus.springsecurity.entity.User;import java.util.List;public interface UserMapper extends BaseMapper <User> { List<User> findAll () ; User findByUsername (String username) ; int save (User user) ; }
UserService 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.cactus.springsecurity.service;import com.cactus.springsecurity.entity.User;import com.cactus.springsecurity.mapper.UserMapper;import org.springframework.beans.factory.annotation.Autowired;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;@Service public class UserService implements UserDetailsService { @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername (String s) throws UsernameNotFoundException { User user = userMapper.findByUsername(s); if (user == null ) { throw new UsernameNotFoundException ("用户不存在" ); } return user; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.cactus.springsecurity.config;import cn.hutool.crypto.digest.DigestUtil;public class JwtConfigure { public static final long EXPIRATION_TIME = 432_000_000 ; public static final String SECRET = "SystemSecret+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24" ; public static final String TOKEN_PREFIX = "Bearer" ; public static final String HEADER_STRING = "Authorization" ; public static void main (String[] args) { System.out.println(DigestUtil.md5Hex("caiqichang" )); } }
JwtTokenUtil 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 package com.cactus.springsecurity.utils;import com.cactus.springsecurity.config.JwtConfigure;import com.cactus.springsecurity.entity.User;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.stereotype.Component;import java.util.Date;import java.util.HashMap;import java.util.Map;@Component public class JwtTokenUtil { private static final long serialVersionUID = -5625635588908941275L ; private static final String CLAIM_KEY_USERNAME = "sub" ; private static final String CLAIM_KEY_CREATED = "created" ; public String getUsernameFromToken (String token) { String username; try { final Claims claims = getClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { username = null ; } return username; } public Date getExpirationDateFromToken (String token) { Date expiration; try { final Claims claims = getClaimsFromToken(token); expiration = claims.getExpiration(); } catch (Exception e) { expiration = null ; } return expiration; } private Claims getClaimsFromToken (String token) { Claims claims; try { claims = Jwts.parser() .setSigningKey( JwtConfigure.SECRET ) .parseClaimsJws(token) .getBody(); } catch (Exception e) { claims = null ; } return claims; } private Date generateExpirationDate () { return new Date (System.currentTimeMillis() + JwtConfigure.EXPIRATION_TIME * 1000 ); } private Boolean isTokenExpired (String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date ()); } private Boolean isCreatedBeforeLastPasswordReset (Date created, Date lastPasswordReset) { return (lastPasswordReset != null && created.before(lastPasswordReset)); } public String generateToken (UserDetails userDetails) { Map<String, Object> claims = new HashMap <>(); claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); claims.put(CLAIM_KEY_CREATED, new Date ()); return generateToken(claims); } String generateToken (Map<String, Object> claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) .signWith(SignatureAlgorithm.HS512, JwtConfigure.SECRET ) .compact(); } public Boolean canTokenBeRefreshed (String token) { return !isTokenExpired(token); } public String refreshToken (String token) { String refreshedToken; try { final Claims claims = getClaimsFromToken(token); claims.put(CLAIM_KEY_CREATED, new Date ()); refreshedToken = generateToken(claims); } catch (Exception e) { refreshedToken = null ; } return refreshedToken; } public Boolean validateToken (String token, UserDetails userDetails) { User user = (User) userDetails; final String username = getUsernameFromToken(token); return ( username.equals(user.getUsername()) && !isTokenExpired(token) ); } }
MyAccessDeniedHandler 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.cactus.springsecurity.handler;import cn.hutool.json.JSONUtil;import com.cactus.springsecurity.entity.response.Result;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.web.access.AccessDeniedHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { httpServletResponse.setCharacterEncoding("UTF-8" ); httpServletResponse.setContentType("text/html;charset=utf-8" ); httpServletResponse.getWriter().write(JSONUtil.toJsonStr(new Result (111 ,null ,"小朋友,你没有访问权限!" ))); } }
MyAuthenticationEntryPoint 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.cactus.springsecurity.handler;import cn.hutool.json.JSONUtil;import com.cactus.springsecurity.entity.response.Result;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.InsufficientAuthenticationException;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Component public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { httpServletResponse.setCharacterEncoding("UTF-8" ); httpServletResponse.setContentType("text/html;charset=utf-8" ); if (e instanceof BadCredentialsException) { httpServletResponse.getWriter().write(JSONUtil.toJsonStr(new Result (401 ,null ,"小朋友,账号或者密码错误" ))); }else if (e instanceof InsufficientAuthenticationException) { httpServletResponse.getWriter().write(JSONUtil.toJsonStr(new Result (402 ,null ,"小朋友,你的token已过期或者不存在!" ))); } else { httpServletResponse.getWriter().write(JSONUtil.toJsonStr(new Result (500 ,null ,"系统繁忙!" ))); } } }
JwtTokenFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.cactus.springsecurity.filter;import com.cactus.springsecurity.config.JwtConfigure;import com.cactus.springsecurity.utils.JwtTokenUtil;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;@Component public class JwtTokenFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Override protected void doFilterInternal (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String authHeader = httpServletRequest.getHeader(JwtConfigure.HEADER_STRING); if (authHeader != null && authHeader.startsWith(JwtConfigure.TOKEN_PREFIX)) { final String authToken = authHeader.substring(JwtConfigure.TOKEN_PREFIX.length()); String username = jwtTokenUtil.getUsernameFromToken(authToken); if (username != null && SecurityContextHolder.getContext().getAuthentication() == null ) { UserDetails userDetails = this .userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(authToken, userDetails)) { UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken ( userDetails, null , userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource ().buildDetails( httpServletRequest)); SecurityContextHolder.getContext().setAuthentication(authentication); } } } filterChain.doFilter(httpServletRequest, httpServletResponse); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.cactus.springsecurity.config;import cn.hutool.crypto.digest.DigestUtil;public class JwtConfigure { public static final long EXPIRATION_TIME = 432_000_000 ; public static final String SECRET = "SystemSecret+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24" ; public static final String TOKEN_PREFIX = "Bearer" ; public static final String HEADER_STRING = "Authorization" ; public static void main (String[] args) { System.out.println(DigestUtil.md5Hex("caiqichang" )); } }
AuthService 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.cactus.springsecurity.service;import com.cactus.springsecurity.entity.User;import com.cactus.springsecurity.entity.response.Result;public interface AuthService { Result register (User userToAdd ) ; String login ( String username, String password ) ; }
AuthServiceImpl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package com.cactus.springsecurity.service.impl;import com.cactus.springsecurity.entity.User;import com.cactus.springsecurity.entity.response.Result;import com.cactus.springsecurity.mapper.UserMapper;import com.cactus.springsecurity.service.AuthService;import com.cactus.springsecurity.utils.JwtTokenUtil;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.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.stereotype.Service;@Service public class AuthServiceImpl implements AuthService { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Autowired private UserMapper userMapper; @Override public String login ( String username, String password ) { UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken ( username, password ); final Authentication authentication = authenticationManager.authenticate(upToken); SecurityContextHolder.getContext().setAuthentication(authentication); final UserDetails userDetails = userDetailsService.loadUserByUsername( username ); final String token = jwtTokenUtil.generateToken(userDetails); return token; } @Override public Result register (User userToAdd ) { final String username = userToAdd.getUsername(); if ( userMapper.findByUsername(username)!=null ) { return new Result (400 ,null ,"账号已存在!" ); } BCryptPasswordEncoder encoder = new BCryptPasswordEncoder (); final String rawPassword = userToAdd.getPassword(); userToAdd.setPassword( encoder.encode(rawPassword) ); int i = userMapper.save(userToAdd); if (i>=0 ){ User user = userMapper.findByUsername(userToAdd.getUsername()); user.setPassword("******" ); return new Result (200 ,user,"注册成功!" ); } else { return new Result (406 ,null ,"发生未知错误,注册失败!" ); } } }
JwtAuthController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.cactus.springsecurity.config;import cn.hutool.crypto.digest.DigestUtil;public class JwtConfigure { public static final long EXPIRATION_TIME = 432_000_000 ; public static final String SECRET = "SystemSecret+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24+d7d8825f2b61d50a48508b17b88b4e24" ; public static final String TOKEN_PREFIX = "Bearer" ; public static final String HEADER_STRING = "Authorization" ; public static void main (String[] args) { System.out.println(DigestUtil.md5Hex("caiqichang" )); } }
TestController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.cactus.springsecurity.controller;import org.springframework.security.access.prepost.PreAuthorize;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestController public class TestController { @PreAuthorize("hasAuthority('ROLE_NORMAL')") @RequestMapping( value="/normal/test", method = RequestMethod.GET ) public String test1 () { return "ROLE_NORMAL /normal/test接口调用成功!" ; } @PreAuthorize("hasAuthority('ROLE_ADMIN')") @RequestMapping( value = "/admin/test", method = RequestMethod.GET ) public String test2 () { return "ROLE_ADMIN /admin/test接口调用成功!" ; } }
SpringsecurityJwtApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.cactus.springsecurity;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication @MapperScan(basePackages = "com.cactus.springsecurity.mapper") public class SpringsecurityJwtApplication { public static void main (String[] args) { SpringApplication.run(SpringsecurityJwtApplication.class, args); } }
测试 测试注册 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 POST http://192.168.77.142:8001/authentication/register { "uid": "1", "username": "cqc1", "password": "cqc1" } result ======> { "code": 200, "data": { "uid": "2", "username": "cqc1", "password": "******", "roles": [ { "id": 2, "roleName": "ROLE_ NORMAL", "description": "ROLE_ NORMAL" } ], "enabled": true, "credentialsNonExpired": true, "accountNonExpired": true, "accountNonLocked": true }, "msg": "注册成功!" }
测试登录 1 2 3 4 5 6 7 8 9 POST http://192.168.77.142:8001/authentication/login?username=cqc1& password=cqc1 { "code": 200, "data": { "token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjcWMxIiwiY3JlYXRlZCI6MTY5NDY3ODAwNDI1NSwiZXhwIjoyMTI2Njc4MDA0fQ.n5BinuMFAF3cLplCpv-M7VdjWoKfW3zAKGFlaNei79DUiZ7GVbExZ-gX01-Iw7Qa0ltIDki5IY1PjVqarOJz3w" }, "msg": "小朋友,登陆成功!" }
测试权限接口 成功状态
1 2 3 4 GET http://192.168.77.142:8001/normal/test Headers ==> Authorization:BearereyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjcWMxIiwiY3JlYXRlZCI6MTY5NDY3ODAwNDI1NSwiZXhwIjoyMTI2Njc4MDA0fQ.n5BinuMFAF3cLplCpv-M7VdjWoKfW3zAKGFlaNei79DUiZ7GVbExZ-gX01-Iw7Qa0ltIDki5IY1PjVqarOJz3w
失败状态
1 2 3 4 GET http://192.168.77.142:8001/admin/test Headers ==> Authorization:BearereyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJjcWMxIiwiY3JlYXRlZCI6MTY5NDY3ODAwNDI1NSwiZXhwIjoyMTI2Njc4MDA0fQ.n5BinuMFAF3cLplCpv-M7VdjWoKfW3zAKGFlaNei79DUiZ7GVbExZ-gX01-Iw7Qa0ltIDki5IY1PjVqarOJz3w