赞
踩
(1)、jwtUtils.java: JWT生成TOKEN
导入依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
生成JWT工具代码:
package com.aliyun.vuelogin.util; import com.aliyun.vuelogin.common.Payload; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.joda.time.DateTime; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; import java.util.UUID; /** * @author: * 生成token以及校验token相关方法 */ 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<Claims> */ private static Jws<Claims> 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 <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> 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 <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> claims = new Payload<>(); claims.setId(body.getId()); claims.setExpiration(body.getExpiration()); return claims; } }
(2)、jsonUtils.java: json和其他类型转化的工具类
package com.aliyun.vuelogin.util; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; import java.util.Map; /** * @author: **/ 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> T toBean(String json, Class<T> tClass) { try { return mapper.readValue(json, tClass); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } public static <E> List<E> toList(String json, Class<E> eClass) { try { return mapper.readValue(json, mapper.getTypeFactory().constructCollectionType(List.class, eClass)); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } public static <K, V> Map<K, V> toMap(String json, Class<K> kClass, Class<V> 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> T nativeRead(String json, TypeReference<T> type) { try { return mapper.readValue(json, type); } catch (IOException e) { logger.error("json解析出错:" + json, e); return null; } } }
(3)、RsaUtils.java:公钥、私钥生成工具类
package com.aliyun.vuelogin.util; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * 根据文件地址 得到公钥 私钥 * @author */ 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); } }
package com.aliyun.vuelogin.common; import com.aliyun.vuelogin.util.RsaUtils; 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; /** * 生成公钥 私钥方法 * */ @ConfigurationProperties("rsa.key") public class RsaKeyProperties { private String pubKeyFile; private PublicKey publicKey; private String priKeyFile; private PrivateKey privateKey; @PostConstruct public void createRsaKey() throws Exception { publicKey = RsaUtils.getPublicKey(pubKeyFile); privateKey = RsaUtils.getPrivateKey(priKeyFile); } public String getPubKeyFile() { return pubKeyFile; } public void setPubKeyFile(String pubKeyFile) { this.pubKeyFile = pubKeyFile; } public PublicKey getPublicKey() { return publicKey; } public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } public String getPriKeyFile() { return priKeyFile; } public void setPriKeyFile(String priKeyFile) { this.priKeyFile = priKeyFile; } public PrivateKey getPrivateKey() { return privateKey; } public void setPrivateKey(PrivateKey privateKey) { this.privateKey = privateKey; } }
package com.aliyun.vuelogin.JwtFilter; import com.aliyun.vuelogin.common.RsaKeyProperties; import com.aliyun.vuelogin.model.SysRole; import com.aliyun.vuelogin.model.SysUser; import com.aliyun.vuelogin.util.JwtUtils; import com.fasterxml.jackson.databind.ObjectMapper; 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.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.HashMap; import java.util.List; import java.util.Map; /** * jwt登录认证过滤器 * */ public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; private RsaKeyProperties prop; public JwtLoginFilter(AuthenticationManager authenticationManager, RsaKeyProperties prop) { this.authenticationManager = authenticationManager; this.prop = prop; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try{ SysUser sysUser = new ObjectMapper().readValue(request.getInputStream(), SysUser.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(); } } protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SysUser sysUser=new SysUser(); sysUser.setUsername(authResult.getName()); sysUser.setRoles((List<SysRole>) authResult.getAuthorities()); String token = JwtUtils.generateTokenExpireInMinutes(sysUser, prop.getPrivateKey(), 24 * 60); response.setHeader("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(); } } }
package com.aliyun.vuelogin.JwtFilter; import com.aliyun.vuelogin.common.Payload; import com.aliyun.vuelogin.common.RsaKeyProperties; import com.aliyun.vuelogin.model.SysUser; import com.aliyun.vuelogin.util.JwtUtils; import com.fasterxml.jackson.databind.ObjectMapper; 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.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; /** * jwt权限验证过滤器 * */ public class JwtVerifyFilter extends BasicAuthenticationFilter { private RsaKeyProperties prop; public JwtVerifyFilter(AuthenticationManager authenticationManager,RsaKeyProperties prop) { super(authenticationManager); this.prop=prop; } protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String header = request.getHeader("Authorization"); System.out.println(header); 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", ""); //验证token是否正确 Payload<SysUser> payload = JwtUtils.getInfoFromToken(token, prop.getPublicKey(), SysUser.class); SysUser userInfo = payload.getUserInfo(); if(userInfo!=null){ UsernamePasswordAuthenticationToken authRequest=new UsernamePasswordAuthenticationToken(userInfo.getUsername(),null,userInfo.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authRequest); chain.doFilter(request, response); } } } }
rsa:
key:
pubKeyFile: D:\work\MyProject\springboot-aliyun\vuelogin\id_key_rsa.pub
priKeyFile: D:\work\MyProject\springboot-aliyun\vuelogin\id_key_rsa
package com.aliyun.vuelogin.security; import com.aliyun.vuelogin.JwtFilter.JwtLoginFilter; import com.aliyun.vuelogin.JwtFilter.JwtVerifyFilter; import com.aliyun.vuelogin.common.RsaKeyProperties; import com.aliyun.vuelogin.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; 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; @EnableWebSecurity @EnableGlobalMethodSecurity(securedEnabled = true) public class SecurityJwtConfig extends WebSecurityConfigurerAdapter { @Autowired SysUserService sysUserService; @Autowired RsaKeyProperties prop; @Bean BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { System.out.println("执行授权方法"); //shiro路径下所有人都可以访问,security只有vip可以访问 http.csrf().disable() .authorizeRequests() .antMatchers("/shiro/*").permitAll() .antMatchers("/security/tologin").permitAll() .antMatchers("/security/*").hasAnyRole("vip") //下面一行代码表示其他所有资源只有认证通过才能访问 .anyRequest().authenticated() //下面添加过滤器 .and().addFilter(new JwtLoginFilter(super.authenticationManager(),prop)) .addFilter(new JwtVerifyFilter(super.authenticationManager(),prop)) //下面禁用session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); //也可以这样写http.csrf().disable();//关闭网站拦截get请求 // //开启---没有权限跳转到登录页面 // http.formLogin() // .loginPage("/security/tologin")//设置为自己写的登录页面 // .usernameParameter("account")//设置前端传的用户名字段名 // .passwordParameter("password")//设置前端传的密码字段名 // .loginProcessingUrl("/login");//设置实际提交用户名和密码的路径,这个“/login”是security自带的,不用自己写 // http.formLogin().successForwardUrl("/security/test"); // http.rememberMe().rememberMeParameter("remember");//开启记住我功能(cookie) // http.logout().logoutSuccessUrl("/security/tologin"); //开启登出(注销) } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { System.out.println("执行认证方法"); /* 下面的代码: auth.inMemoryAuthentication().withUser("Eric").password("123456").roles("vip") .and().withUser("root").password("root").roles("vvip"); 正常是从数据库中读取,这里仅模拟一下用户名和密码和权限 注意,这里如果springboot版本过高,即使登陆了,也会报500 PasswordEncoder(密码未加密) 的错误 在security5中新增了许多内置加密的方法,下面使用其中一种 .passwordEncoder(new BCryptPasswordEncoder()) * */ /**下面三行代码是使用内存 设置临时用户名和密码 * auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()) * .withUser("Eric").password(new BCryptPasswordEncoder().encode("1")).roles("vip") * .and().withUser("root").password(new BCryptPasswordEncoder().encode("root")).roles("vvip"); */ //注意 此时权限role已经在sysUserService的loadUserByUsername返回值里了,但数据库表里必须把权限字段加上”ROLE_“的前缀 // 例如:数据库权限字段值为ROLE_vip才能验证.hasRole("vip")的vip, 否则从数据库里查询的权限不能识别 auth.userDetailsService(sysUserService).passwordEncoder(passwordEncoder()); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。