网易首页 > 网易号 > 正文 申请入驻

Spring Security + JWT 实现单点登录,还有谁不会??

0
分享至

  本文我们来看下 SpringSecurity + JWT 实现单点登录操作,本文 2W 字,预计阅读时间 30 min,文章提供了代码骨架,建议收藏。

  一、什么是单点登陆

  单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统

  二、简单的运行机制

  单点登录的机制其实是比较简单的,用一个现实中的例子做比较。某公园内部有许多独立的景点,游客可以在各个景点门口单独买票。

  对于需要游玩所有的景点的游客,这种买票方式很不方便,需要在每个景点门口排队买票,钱包拿 进拿出的,容易丢失,很不安全。

  于是绝大多数游客选择在大门口买一张通票(也叫套票),就可以玩遍所有的景点而不需要重新再买票。他们只需要在每个景点门 口出示一下刚才买的套票就能够被允许进入每个独立的景点。

  单点登录的机制也一样,如下图所示,

  用户认证:这一环节主要是用户向认证服务器发起认证请求,认证服务器给用户返回一个成功的令牌token,主要在认证服务器中完成,即图中的认证系统,注意认证系统只能有一个。

  身份校验:这一环节是用户携带token去访问其他服务器时,在其他服务器中要对token的真伪进行检验,主要在资源服务器中完成,即图中的应用系统2 3

  三、JWT介绍

  概念说明

  从分布式认证流程中,我们不难发现,这中间起最关键作用的就是token,token的安全与否,直接关系到系统的健壮性,这里我们选择使用JWT来实现token的生成和校验。

  JWT,全称JSON Web Token,官网地址:https://jwt.io,是一款出色的分布式身份校验方案。可以生成token,也可以解析检验token。

  JWT生成的token由三部分组成:

  头部:主要设置一些规范信息,签名部分的编码格式就在头部中声明。

  载荷:token中存放有效信息的部分,比如用户名,用户角色,过期时间等,但是不要放密码,会泄露!

  签名:将头部与载荷分别采用base64编码后,用“.”相连,再加入盐,最后使用头部声明的编码类型进行编码,就得到了签名。

  JWT生成token的安全性分析

  从JWT生成的token组成上来看,要想避免token被伪造,主要就得看签名部分了,而签名部分又有三部分组成,其中头部和载荷的base64编码,几乎是透明的,毫无安全性可言,那么最终守护token安全的重担就落在了加入的盐上面了!

  试想:如果生成token所用的盐与解析token时加入的盐是一样的。岂不是类似于中国人民银行把人民币防伪技术公开了?大家可以用这个盐来解析token,就能用来伪造token。这时,我们就需要对盐采用非对称加密的方式进行加密,以达到生成token与校验token方所用的盐不一致的安全效果!

  非对称加密RSA介绍

  基本原理:同时生成两把密钥:私钥和公钥,私钥隐秘保存,公钥可以下发给信任客户端

  私钥加密,持有私钥或公钥才可以解密

  公钥加密,持有私钥才可解密

  优点:安全,难以破解

  缺点:算法比较耗时,为了安全,可以接受

  历史:三位数学家Rivest、Shamir 和 Adleman 设计了一种算法,可以实现非对称加密。这种算法用他们三个人的名字缩写:RSA。

  1.认证思路分析

  SpringSecurity主要是通过过滤器来实现功能的!我们要找到SpringSecurity实现认证和校验身份的过滤器!

  回顾集中式认证流程

  用户认证:使用UsernamePasswordAuthenticationFilter过滤器中attemptAuthentication方法实现认证功能,该过滤器父类中successfulAuthentication方法实现认证成功后的操作。

  身份校验:使用BasicAuthenticationFilter过滤器中doFilterInternal方法验证是否登录,以决定能否进入后续过滤器。

  分析分布式认证流程

  用户认证:

  由于分布式项目,多数是前后端分离的架构设计,我们要满足可以接受异步post的认证请求参数,需要修改UsernamePasswordAuthenticationFilter过滤器中attemptAuthentication方法,让其能够接收请求体。

  另外,默认successfulAuthentication方法在认证通过后,是把用户信息直接放入session就完事了,现在我们需要修改这个方法,在认证通过后生成token并返回给用户。

  身份校验:

  原来BasicAuthenticationFilter过滤器中doFilterInternal方法校验用户是否登录,就是看session中是否有用户信息,我们要修改为,验证用户携带的token是否合法,并解析出用户信息,交给SpringSecurity,以便于后续的授权功能可以正常使用。

  2.具体实现

  为了演示单点登录的效果,我们设计如下项目结构

  因为本案例需要创建多个系统,所以我们使用maven聚合工程来实现,首先创建一个父工程,导入springboot的父依赖即可。

  Spring Boot 基础就不介绍了,推荐下这个实战教程:https://github.com/javastacks/spring-boot-best-practice

  
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE

  2.2公共工程创建

  然后创建一个common工程,其他工程依赖此系统

  导入JWT相关的依赖

  io.jsonwebtoken
jjwt-api
0.10.7

  io.jsonwebtoken
jjwt-impl
0.10.7
runtime

  io.jsonwebtoken
jjwt-jackson
0.10.7
runtime

  com.fasterxml.jackson.core
jackson-databind
2.9.9

  org.springframework.boot
spring-boot-starter-logging

  joda-time
joda-time

  org.projectlombok
lombok

  org.springframework.boot
spring-boot-starter-test

  创建相关的工具类,

  Payload

  @Data
public class Payload {
private String id;
private T userInfo;
private Date expiration;
}

  JsonUtils

  public class JsonUtils {

  public static final ObjectMapper mapper = new ObjectMapper();

  private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);

  public static String toString(Object obj) {
if (obj == null) {
return null;
}
if (obj.getClass() == String.class) {
return (String) obj;
}
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
logger.error("json序列化出错:" + obj, e);
return null;
}
}

  public static T toBean(String json, Class tClass) {
try {
return mapper.readValue(json, tClass);
} catch (IOException e) {
logger.error( "json解析出错:" + json, e);
return null;
}
}

  public static List toList(String json, Class eClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass));
} catch (IOException e) {
logger.error( "json解析出错:" + json, e);
return null;
}
}

  public static Map toMap(String json, Class kClass, Class vClass) {
try {
return mapper.readValue(json, mapper.getTypeFactory().constructMapType(Map.class, kClass, vClass));
} catch (IOException e) {
logger.error( "json解析出错:" + json, e);
return null;
}
}

  public static T nativeRead(String json, TypeReference type) {
try {
return mapper.readValue(json, type);
} catch (IOException e) {
logger.error( "json解析出错:" + json, e);
return null;
}
}
}

  JwtUtils

  public class JwtUtils {

  private static final String JWT_PAYLOAD_USER_KEY = "user";

  /**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位分钟
* @return JWT
*/
public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusMinutes(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}

  /**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间,单位秒
* @return JWT
*/
public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtils.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}

  /**
* 公钥解析token
*
* @param token 用户请求中的token
* @param publicKey 公钥
* @return Jws
*/
private static Jws parserToken(String token, PublicKey publicKey) {
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}

  private static String createJTI() {
return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
}

  /**
* 获取token中的用户信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @ return 用户信息
*/
public static Payload getInfoFromToken(String token, PublicKey publicKey, Class userType) {
Jws claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload claims = new Payload<>();
claims.setId(body.getId());
claims.setUserInfo(JsonUtils.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(), userType));
claims.setExpiration(body.getExpiration());
return claims;
}

  /**
* 获取token中的载荷信息
*
* @param token 用户请求中的令牌
* @param publicKey 公钥
* @ return 用户信息
*/
public static Payload getInfoFromToken(String token, PublicKey publicKey) {
Jws claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload claims = new Payload<>();
claims.setId(body.getId());
claims.setExpiration(body.getExpiration());
return claims;
}
}

  RsaUtils

  public class RsaUtils {

  private static final int DEFAULT_KEY_SIZE = 2048;
/**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPublicKey(bytes);
}

  /**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPrivateKey(bytes);
}

  /**
* 获取公钥
*
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(byte[] bytes) throws Exception {
bytes = Base64.getDecoder().decode(bytes);
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}

  /**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
bytes = Base64.getDecoder().decode(bytes);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
}

  /**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret, int keySize) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
keyPairGenerator.initialize(Math.max(keySize, DEFAULT_KEY_SIZE), secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
publicKeyBytes = Base64.getEncoder().encode(publicKeyBytes);
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
privateKeyBytes = Base64.getEncoder().encode(privateKeyBytes);
writeFile(privateKeyFilename, privateKeyBytes);
}

  private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes(new File(fileName).toPath());
}

  private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File(destPath);
if (!dest.exists()) {
dest.createNewFile();
}
Files.write(dest.toPath(), bytes);
}
}

  在通用子模块中编写测试类生成rsa公钥和私钥

  public class JwtTest {
private String privateKey = "c:/tools/auth_key/id_key_rsa";

  private String publicKey = "c:/tools/auth_key/id_key_rsa.pub";

  @Test
public void test1() throws Exception{
RsaUtils.generateKey(publicKey,privateKey,"dpb",1024);
}

  }

  接下来我们创建我们的认证服务。

  导入相关的依赖

  org.springframework.boot
spring-boot-starter-web

  org.springframework.boot
spring-boot-starter-security

  security-jwt-common
com.dpb
1.0-SNAPSHOT

  mysql
mysql-connector-java
5.1.47

  org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0

  com.alibaba
druid
1.1.10

  org.springframework.boot
spring-boot-configuration-processor
true

  创建配置文件

  spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/srm
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
type-aliases-package: com.dpb.domain
mapper-locations: classpath:mapper/*.xml
logging:
level:
com.dpb: debug
rsa:
key:
pubKeyFile: c:\tools\auth_key\id_key_rsa.pub
priKeyFile: c:\tools\auth_key\id_key_rsa

  提供公钥私钥的配置类

  package com.dpb.config;

  import com.dpb.utils.RsaUtils;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

  import javax.annotation.PostConstruct;
import java.security.PrivateKey;
import java.security.PublicKey;

  /**
* @program: springboot-54-security-jwt-demo
* @description:
* @author: 波波烤鸭
* @create: 2019-12-03 11:25
*/
@Data
@ConfigurationProperties(prefix = "rsa.key")
public class RsaKeyProperties {

  private String pubKeyFile;
private String priKeyFile;

  private PublicKey publicKey;
private PrivateKey privateKey;

  /**
* 系统启动的时候触发
* @throws Exception
*/
@PostConstruct
public void createRsaKey() throws Exception {
publicKey = RsaUtils.getPublicKey(pubKeyFile);
privateKey = RsaUtils.getPrivateKey(priKeyFile);
}

  }

  创建启动类

  /**
* @program: springboot-54-security-jwt-demo
* @description: 启动类
* @author: 波波烤鸭
* @create: 2019-12-03 11:23
*/
@SpringBootApplication
@MapperScan("com.dpb.mapper")
@EnableConfigurationProperties(RsaKeyProperties.class)
public class App {

  public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}

  完成数据认证的逻辑

  pojo

  package com.dpb.domain;

  import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;

  /**
* @program: springboot-54-security-jwt-demo
* @description:
* @author: 波波烤鸭
* @create: 2019-12-03 15:21
*/
@Data
public class RolePojo implements GrantedAuthority {

  private Integer id;
private String roleName;
private String roleDesc;

  @JsonIgnore
@Override
public String getAuthority() {
return roleName;
}
}

  package com.dpb.domain;

  import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
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;

  /**
* @program: springboot-54-security-jwt-demo
* @description:
* @author: 波波烤鸭
* @create: 2019-12-03 11:33
*/
@Data
public class UserPojo implements UserDetails {

  private Integer id;

  private String username;

  private String password;

  private Integer status;

  private List roles;

  @JsonIgnore
@Override
public Collection getAuthorities() {
List auth = new ArrayList<>();
auth.add(new SimpleGrantedAuthority( "ADMIN"));
return auth;
}

  @Override
public String getPassword() {
return this.password;
}

  @Override
public String getUsername() {
return this.username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isEnabled() {
return true;
}
}

  Mapper接口

  public interface UserMapper {
public UserPojo queryByUserName(@Param("userName") String userName);
}

  Mapper映射文件

  "1.0" encoding="UTF-8" ?>
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"com.dpb.mapper.UserMapper">
"queryByUserName" resultType="UserPojo"> select * from t_user where username = #{userName} Service public interface UserService extends UserDetailsService {}@Service@Transactionalpublic class UserServiceImpl implements UserService { @Autowired private UserMapper mapper; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { UserPojo user = mapper.queryByUserName(s); return user; }} 自定义认证过滤器 扩展: package com.dpb.filter;import com.dpb.config.RsaKeyProperties;import com.dpb.domain.RolePojo;import com.dpb.domain.UserPojo;import com.dpb.utils.JwtUtils;import com.fasterxml.jackson.databind.ObjectMapper;import net.bytebuddy.agent.builder.AgentBuilder;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.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;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;import java.io.PrintWriter;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/** * @program: springboot-54-security-jwt-demo * @description: * @author: 波波烤鸭 * @create: 2019-12-03 11:57 */public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; private RsaKeyProperties prop; public TokenLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) { this.authenticationManager = authenticationManager; this.prop = prop; } public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { UserPojo sysUser = new ObjectMapper().readValue(request.getInputStream(), UserPojo.class); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword()); return authenticationManager.authenticate(authRequest); }catch (Exception e){ try { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); PrintWriter out = response.getWriter(); Map resultMap = new HashMap(); resultMap.put("code", HttpServletResponse.SC_UNAUTHORIZED); resultMap.put("msg", "用户名或密码错误!"); out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); }catch (Exception outEx){ outEx.printStackTrace(); } throw new RuntimeException(e); } } public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { UserPojo user = new UserPojo(); user.setUsername(authResult.getName()); user.setRoles((List )authResult.getAuthorities()); String token = JwtUtils.generateTokenExpireInMinutes(user, prop.getPrivateKey(), 24 * 60); response.addHeader( "Authorization", "Bearer "+token); try { response.setContentType( "application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter out = response.getWriter(); Map resultMap = new HashMap(); resultMap.put( "code", HttpServletResponse.SC_OK); resultMap.put( "msg", "认证通过!"); out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); }catch (Exception outEx){ outEx.printStackTrace(); } } } 自定义校验token的过滤器 package com.dpb.filter;import com.dpb.config.RsaKeyProperties;import com.dpb.domain.Payload;import com.dpb.domain.UserPojo;import com.dpb.utils.JwtUtils;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;import java.util.HashMap;import java.util.Map;/** * @program: springboot-54-security-jwt-demo * @description: * @author: 波波烤鸭 * @create: 2019-12-03 12:39 */public class TokenVerifyFilter extends BasicAuthenticationFilter { private RsaKeyProperties prop; public TokenVerifyFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) { super(authenticationManager); this.prop = prop; } public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader("Authorization"); if (header == null || !header.startsWith("Bearer ")) { //如果携带错误的token,则给用户提示请登录! chain.doFilter(request, response); response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_FORBIDDEN); PrintWriter out = response.getWriter(); Map resultMap = new HashMap(); resultMap.put("code", HttpServletResponse.SC_FORBIDDEN); resultMap.put("msg", "请登录!"); out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); } else { //如果携带了正确格式的token要先得到token String token = header.replace("Bearer ", ""); //验证tken是否正确 Payload payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), UserPojo.class); UserPojo user = payload.getUserInfo(); if(user!=null){ UsernamePasswordAuthenticationToken authResult = new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); } } } } 编写SpringSecurity的配置类 package com.dpb.config;import com.dpb.filter.TokenLoginFilter;import com.dpb.filter.TokenVerifyFilter;import com.dpb.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;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.crypto.bcrypt.BCryptPasswordEncoder;/** * @program: springboot-54-security-jwt-demo * @description: * @author: 波波烤鸭 * @create: 2019-12-03 12:41 */@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(securedEnabled=true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Autowired private RsaKeyProperties prop; @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } //指定认证对象的来源 public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } //SpringSecurity配置信息 public void configure(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() .antMatchers("/user/query").hasAnyRole("ADMIN") .anyRequest() .authenticated() .and() .addFilter(new TokenLoginFilter(super.authenticationManager(), prop)) .addFilter(new TokenVerifyFilter(super.authenticationManager(), prop)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }} 启动服务测试 启动服务 通过Postman来访问测试。 根据token信息我们访问其他资源 2.4资源系统创建 说明 资源服务可以有很多个,这里只拿产品服务为例,记住,资源服务中只能通过公钥验证认证。不能签发token!创建产品服务并导入jar包根据实际业务导包即可,咱们就暂时和认证服务一样了。 接下来我们再创建一个资源服务 导入相关的依赖 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security security-jwt-common com.dpb 1.0-SNAPSHOT mysql mysql-connector-java 5.1.47 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.0 com.alibaba druid 1.1.10 org.springframework.boot spring-boot-configuration-processor true 编写产品服务配置文件 切记这里只能有公钥地址! server: port: 9002spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/srm username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSourcemybatis: type-aliases-package: com.dpb.domain mapper-locations: classpath:mapper/*.xmllogging: level: com.dpb: debugrsa: key: pubKeyFile: c:\tools\auth_key\id_key_rsa.pub 编写读取公钥的配置类 @Data@ConfigurationProperties(prefix = "rsa.key")public class RsaKeyProperties { private String pubKeyFile; private PublicKey publicKey; /** * 系统启动的时候触发 * @throws Exception */ @PostConstruct public void createRsaKey() throws Exception { publicKey = RsaUtils.getPublicKey(pubKeyFile); }} 编写启动类 @SpringBootApplication@MapperScan("com.dpb.mapper")@EnableConfigurationProperties(RsaKeyProperties.class)public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); }} 复制认证服务中,用户对象,角色对象和校验认证的接口 复制认证服务中的相关内容即可 复制认证服务中SpringSecurity配置类做修改 @Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(securedEnabled=true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Autowired private RsaKeyProperties prop; @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } //指定认证对象的来源 public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } //SpringSecurity配置信息 public void configure(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests() //.antMatchers("/user/query").hasAnyRole("USER") .anyRequest() .authenticated() .and() .addFilter(new TokenVerifyFilter(super.authenticationManager(), prop)) // 禁用掉session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); }} 去掉“增加自定义认证过滤器”即可! 编写产品处理器 @RestController@RequestMapping("/user")public class UserController { @RequestMapping("/query") public String query(){ return "success"; } @RequestMapping("/update") public String update(){ return "update"; }}

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
告别战!主帅遭驱逐,痛击上海男篮,CBA黑马关键战:4大外援爆发

告别战!主帅遭驱逐,痛击上海男篮,CBA黑马关键战:4大外援爆发

话体坛
2024-12-02 22:49:15
永远不要在别人面前,随便说出以下6件事,不然后悔莫及!

永远不要在别人面前,随便说出以下6件事,不然后悔莫及!

娱乐洞察点点
2024-11-28 12:02:47
知名女星确诊“精神分裂”,全网妈妈:天塌了,我也是这个症状……

知名女星确诊“精神分裂”,全网妈妈:天塌了,我也是这个症状……

In风尚
2024-11-29 21:39:19
形势有多严峻,各种暗示苦日子已经开始了

形势有多严峻,各种暗示苦日子已经开始了

无根树花正孤
2024-10-16 21:09:09
你见过最主动的女生是什么样的?网友们也太会了!

你见过最主动的女生是什么样的?网友们也太会了!

滑稽斑马呀
2024-06-18 18:38:21
意甲最新积分战报 最没用的0败!尤文难进前5 那不勒斯多国米4分

意甲最新积分战报 最没用的0败!尤文难进前5 那不勒斯多国米4分

狂言体育
2024-12-02 11:53:30
足球报:错过两次亚洲主办资格,中国申办世界杯最快也得2046年

足球报:错过两次亚洲主办资格,中国申办世界杯最快也得2046年

直播吧
2024-12-02 09:35:24
64队抽签,曼联却抽到了阿森纳,足总杯这一轮抽签有猫腻?

64队抽签,曼联却抽到了阿森纳,足总杯这一轮抽签有猫腻?

星耀国际足坛
2024-12-03 08:29:39
三只羊供应链服务公司经营异常

三只羊供应链服务公司经营异常

界面新闻
2024-12-02 10:11:11
爆料:沙特放弃了,还提到中国

爆料:沙特放弃了,还提到中国

环球时报国际
2024-11-30 13:26:50
从全国瞩目的“锁边”工程,看陕西“消失的沙漠”……

从全国瞩目的“锁边”工程,看陕西“消失的沙漠”……

城记言成己
2024-12-02 15:19:12
赛季至今G联盟场均19.6分!费根:火箭将惠特摩尔下放至毒蛇队

赛季至今G联盟场均19.6分!费根:火箭将惠特摩尔下放至毒蛇队

直播吧
2024-12-03 05:56:08
果然出事了!美军现身俄境内?普京下死命令,拜登都不敢要个说法

果然出事了!美军现身俄境内?普京下死命令,拜登都不敢要个说法

奇闻异评
2024-12-02 11:58:07
史诗级降雨将澳大利亚内陆变成巨大的湿地网络

史诗级降雨将澳大利亚内陆变成巨大的湿地网络

cnBeta.COM
2024-12-01 19:28:07
宝马X6在中国遭遇大降价,还看啥X5L?

宝马X6在中国遭遇大降价,还看啥X5L?

沙雕小琳琳
2024-12-02 19:24:08
油价一夜“倾泻”,全国92号汽油今年“次新低”后,12月4日调价

油价一夜“倾泻”,全国92号汽油今年“次新低”后,12月4日调价

猪友巴巴
2024-12-01 14:36:07
特拉华州法官维持原判:重申特斯拉须撤销马斯克巨额薪酬方案

特拉华州法官维持原判:重申特斯拉须撤销马斯克巨额薪酬方案

IT之家
2024-12-03 07:36:10
岳飞被杀前大喊:我无罪!狱卒冷冷吐出6个字,岳飞顿时哑口无言

岳飞被杀前大喊:我无罪!狱卒冷冷吐出6个字,岳飞顿时哑口无言

猫眼观史
2024-11-04 21:23:44
安徽一医学院原院长被查

安徽一医学院原院长被查

安庆发布
2024-12-03 08:42:45
刘晓庆的瓜愈演愈烈!小男友再爆猛料私密视频流出,他得寸进尺了

刘晓庆的瓜愈演愈烈!小男友再爆猛料私密视频流出,他得寸进尺了

辣条小剧场
2024-11-09 06:22:17
2024-12-03 09:51:00
IT架构师联盟
IT架构师联盟
IT架构实战分享
711文章数 7653关注度
往期回顾 全部

科技要闻

英特尔CEO基辛格“下课”,立即生效!

头条要闻

媒体:本届立陶宛政府在进入倒计时之际 又来挑衅中方

头条要闻

媒体:本届立陶宛政府在进入倒计时之际 又来挑衅中方

体育要闻

二轮秀场均20+ 他硬生生练成了CBA最强

娱乐要闻

黄子韬徐艺洋官宣结婚,超般配!

财经要闻

刘世锦:扩大消费需求要找准重点或痛点

汽车要闻

小米汽车:11月交付继续超2万辆 全年冲刺13万辆

态度原创

本地
房产
亲子
旅游
游戏

本地新闻

云游中国|来伦布夏果感受充满Passion的人生

房产要闻

海口楼市开启大反攻!10盘爆卖千套,11月销量榜曝光!

亲子要闻

妈妈记录宝宝给猫咪喂饭,一勺一勺的猫咪喂食

旅游要闻

四川一网红地突发雪崩,有人被埋?紧急提醒

Digital Foundry证实索尼正在开发一款新掌机的说法

无障碍浏览 进入关怀版