一、数据库
二、controller
package cn.**.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.***.security.controller
* @Author: huat
* @Date: 2019/12/12 14:56
* @Version: 1.0
*/
@RestController
public class UserController {
@RequestMapping("/index")
public String login(){
return "index";
}
@RequestMapping("test")
public String test(){
return "hello test";
}
}
三、service
package cn.**.service;
import cn.**.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.**.utile
* @Author: huat
* @Date: 2019/12/12 14:48
* @Version: 1.0
*/
@Service//将这个类注入到spring容器中
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return userDao.getUser(s);
}
四、dao
package cn.**.dao;
import cn.**.entity.AuthorityUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.**.security.dao
* @Author: huat
* @Date: 2019/12/12 15:04
* @Version: 1.0
*/
@Mapper
public interface UserDao {
/**
* 根据用户名查询角色
* @param username 用户名
* @return
*/
AuthorityUser getUser(@Param("username") String username);
/**
* 添加用户
* @param username 用户名
* @param password 密码
* @return
*/
int saveUser(@Param("username") String username, @Param("password") String password);
/**
* 保存token
* @param id
* @param token
* @return
*/
int updateUserToken(@Param("id")int id,@Param("token")String token);
/**
* 根据用户id返回token
* @param id
* @return 用户token
*/
String getTokenById(@Param("id")int id);
}
dao层mapper
/p>
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select u.id id,u.password password, u.username username,r.id rid,r.role_name,r.role_name_CN from authority_user u
left join authority_user_role ur
on ur.user_id=u.id
left join authority_role r
on ur.role_id=r.id
where username=#{username}
insert into authority_user(username,password) values(#{username},#{password})
update authority_user set token=#{token} where id=#{id}
select token from authority_user where id=#{id}
五、entity
AuthorityUser
package cn.**.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.**.security.entity
* @Author: huat
* @Date: 2019/12/12 15:12
* @Version: 1.0
* 创建实体类第一种方式
*/
public class AuthorityUser implements UserDetails {
private int id;
private String username;
private String password;
private List authorityRoles;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List getAuthorityRoles() {
return authorityRoles;
}
public void setAuthorityRoles(List authorityRoles) {
this.authorityRoles = authorityRoles;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public Collection extends GrantedAuthority> getAuthorities() {
return authorityRoles;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isAccountNonExpired() {
return true;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isAccountNonLocked() {
return true;
}
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 当前用户是否可用
* @return
*/
@Override
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
public boolean isEnabled() {
return true;
}
}
AuthorityRole
package cn.**.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.**.security.entity
* @Author: huat
* @Date: 2019/12/12 16:14
* @Version: 1.0
*/
public class AuthorityRole implements GrantedAuthority {
private int rid;
private String roleName;
private String roleNameCN;
public int getRid() {
return rid;
}
public void setRid(int rid) {
this.rid = rid;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleNameCN() {
return roleNameCN;
}
public void setRoleNameCN(String roleNameCN) {
this.roleNameCN = roleNameCN;
}
@JsonIgnore//忽略此属性 转成json字符串时不进行转换
@Override
public String getAuthority() {
return roleName;
}
}
Payload
package cn.**.entity;
import java.util.Date;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.entity
* @Author: huat
* @Date: 2019/12/24 13:13
* @Version: 1.0
*/
public class Payload {
private String id;
private T userInfo;
private Date expiration;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public T getUserInfo() {
return userInfo;
}
public void setUserInfo(T userInfo) {
this.userInfo = userInfo;
}
public Date getExpiration() {
return expiration;
}
public void setExpiration(Date expiration) {
this.expiration = expiration;
}
}
六、filter
JwtTokenFilter
package cn.**.filter;
import cn.**.dao.UserDao;
import cn.**.entity.AuthorityRole;
import cn.**.entity.AuthorityUser;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
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;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.filter
* @Author: huat
* @Date: 2019/12/24 17:04
* @Version: 1.0
*/
public class JwtTokenFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authentication;
private RsaKeyProperties rsaKeyProperties;
private UserDao userDao;
public JwtTokenFilter(AuthenticationManager authentication, RsaKeyProperties rsaKeyProperties,UserDao userDao) {
this.authentication = authentication;
this.rsaKeyProperties = rsaKeyProperties;
this.userDao=userDao;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try {
//从ajax请求流读取参数,转换成AuthorityUser实体类
AuthorityUser authorityUser= new ObjectMapper().readValue(request.getInputStream(), AuthorityUser.class);
//参数是前台传过来的
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorityUser.getUsername(), authorityUser.getPassword());
return authentication.authenticate(authRequest);
} catch (IOException e) {
e.printStackTrace();
try {
response.setContentType("application/json;charset=utf-8");
//返回401状态码 权限不足
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
//响应流
PrintWriter out=response.getWriter();
//封装返回数据
Map resultMap=new HashMap();
resultMap.put("code",HttpServletResponse.SC_UNAUTHORIZED);
resultMap.put("data","");
resultMap.put("msg","用户名密码错误");
//new ObjectMapper().writeValueAsString(resultMap)将map转成json
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
}catch (Exception outEX){
outEX.printStackTrace();
}
throw new RuntimeException(e);
}
}
@Override
public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
AuthorityUser user=(AuthorityUser)authResult.getPrincipal();
/* user.setId(((AuthorityUser)authResult.getPrincipal()).getId());
user.setUsername(authResult.getName());
user.setAuthorityRoles((List) authResult.getAuthorities());*/
String token= JwtUtil.generateTokenExpireInMinutes(user,rsaKeyProperties.getPrivateKey(),24 * 60);
response.setHeader("Authorization","bearer "+token);
try {
//将token保存到数据库当中
int reslut=userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),token);
//如果保存成功给用户返回
if(reslut>0){
response.setContentType("application/json;charset=utf-8");
//返回200状态码
response.setStatus(HttpServletResponse.SC_OK);
//响应流
PrintWriter out=response.getWriter();
//封装返回数据
Map resultMap=new HashMap();
resultMap.put("code",HttpServletResponse.SC_OK);
resultMap.put("data","");
resultMap.put("msg","认证通过");
//new ObjectMapper().writeValueAsString(resultMap)将map转成json
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
}
}catch (Exception outEX){
outEX.printStackTrace();
userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),"");
}
}
}
JwtVerifyFileter
package cn.**.filter;
import cn.**.entity.AuthorityUser;
import cn.**.entity.Payload;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
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;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.filter
* @Author: huat
* @Date: 2019/12/24 18:03
* @Version: 1.0
*/
public class JwtVerifyFileter extends BasicAuthenticationFilter {
private RsaKeyProperties rsaKeyProperties;
public JwtVerifyFileter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) {
super(authenticationManager);
this.rsaKeyProperties = rsaKeyProperties;
}
@Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// request.getHeader("Authorization")这个括号中的值需要和
// JwtTokenFilter这个类中successfulAuthentication方法中
// response.setHeader("Authorization","bearer "+token);的括号中的第一个值一致
String header = request.getHeader("Authorization");
// header.startsWith("bearer ")这个括号中的值需要和
// JwtTokenFilter这个类中successfulAuthentication方法中
// response.setHeader("Authorization","bearer "+token);的括号中的第二个值加号前一致
if (header == null || !header.startsWith("bearer ")) {
//如果没有登陆提示用户进行登陆
response.setContentType("application/json;charset=utf-8");
//返回403状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//响应流
PrintWriter out = response.getWriter();
//封装返回数据
Map resultMap = new HashMap();
resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
resultMap.put("data", "");
resultMap.put("msg", "请登录");
//new ObjectMapper().writeValueAsString(resultMap)将map转成json
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
chain.doFilter(request, response);
} else {
//获取token
String token=header.replace("bearer ","");
//验证token是否正确
Payload payload=JwtUtil.getInfoFromToken(token,rsaKeyProperties.getPublicKey(), AuthorityUser.class);
AuthorityUser user=payload.getUserInfo();
if(null!=user){
UsernamePasswordAuthenticationToken authResult=new UsernamePasswordAuthenticationToken(user.getUsername(),null,user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authResult);
chain.doFilter(request, response);
}
}
}
}
七、util
JsonUtil
package cn.**.util;
import cn.**.entity.AuthorityRole;
import cn.**.entity.AuthorityUser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.User;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/24 10:58
* @Version: 1.0
*/
@Slf4j
public class JsonUtil {
public static final ObjectMapper objectMapper=new ObjectMapper();
/**
* 将对象转为json
* @param object 对象
* @return json
*/
public static String toString(Object object) {
if(null==object){
return null;
}
if(object.getClass()==String.class){
return (String)object;
}
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
log.info(object+"序列化出错"+e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 将json转换成对象
* @param json json字符串
* @param tClass 类
* @param 泛型
* @return 返回对象
*/
public static T toBean(String json,Class tClass){
try {
return objectMapper.readValue(json,tClass);
} catch (IOException e) {
log.info(json+"转对象失败"+e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 将json转换成集合对象
* @param json json字符串
* @param eClass 类 集合对象
* @param 泛型
* @return 返回对象
*/
public static List toList(String json,Class eClass){
try {
return objectMapper.readValue(json,objectMapper.getTypeFactory().constructCollectionType(List.class,eClass));
} catch (IOException e) {
log.info(json+"转对象失败"+e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 将json转换成Map对象
* @param json json字符串
* @param kClass 键对象
* @param vClass 值对象
* @param 键的类型
* @param 值的类型
* @return map对象
*/
public static Map toMap(String json,Class kClass,Class vClass){
try {
return objectMapper.readValue(json,objectMapper.getTypeFactory().constructMapType(Map.class,kClass,vClass));
} catch (IOException e) {
log.info(json+"转对象失败"+e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* 复杂类型转换 泛型套泛型
* @param json json字符串
* @param type 返回类型
* @param
* @return 返回对象
*/
public static T nativeRead(String json, TypeReference type){
try {
return objectMapper.readValue(json,type);
} catch (IOException e) {
log.info(json+"转对象失败"+e.getMessage());
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
//测试nativeRead方法
String json="{\"id\":1,\"username\":\"张三\",\"password\":\"123\",\"authorityRoles\":[{\"rid\":0,\"roleName\":\"张三0\",\"roleNameCN\":null},{\"rid\":1,\"roleName\":\"张三1\",\"roleNameCN\":null},{\"rid\":2,\"roleName\":\"张三2\",\"roleNameCN\":null},{\"rid\":3,\"roleName\":\"张三3\",\"roleNameCN\":null},{\"rid\":4,\"roleName\":\"张三4\",\"roleNameCN\":null}]}\n";
JsonUtil.nativeRead(json,new TypeReference(){});
}
}
JwtUtil
package cn.**.util;
import cn.**.entity.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;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/24 12:44
* @Version: 1.0
*/
public class JwtUtil {
private final static String JWT_PAYLOAD_USER_KEY = "user";
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间 单位分钟
* @return
*/
public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusMinutes(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
/**
* 私钥加密token
*
* @param userInfo 载荷中的数据
* @param privateKey 私钥
* @param expire 过期时间 单位秒
* @return
*/
public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) {
return Jwts.builder()
.claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo))
.setId(createJTI())
.setExpiration(DateTime.now().plusSeconds(expire).toDate())
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
}
private static String createJTI() {
return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
}
/**
* 公钥解析token
* @param token 用户请求的令牌
* @param publicKey 公钥
* @return
*/
private static Jws parserToken(String token, PublicKey publicKey){
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
}
/**
* 获取token中的载荷信息
* @param token 用户请求的令牌
* @param publicKey 公钥
* @param userType 用户类型
* @param
* @return 用户信息
*/
public static Payload getInfoFromToken(String token, PublicKey publicKey, Class userType) {
Jws claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload payload = new Payload();
payload.setId(body.getId());
payload.setUserInfo(JsonUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(),userType));
payload.setExpiration(body.getExpiration());
return payload;
}
/**
* 获取token中的载荷信息
* @param token 用户请求的令牌
* @param publicKey 公钥
* @param
* @return 用户信息
*/
public static Payload getInfoFromToken(String token, PublicKey publicKey) {
Jws claimsJws = parserToken(token, publicKey);
Claims body = claimsJws.getBody();
Payload payload = new Payload();
payload.setId(body.getId());
payload.setExpiration(body.getExpiration());
return payload;
}
}
RsaKeyProperties
package cn.**.util;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/24 15:58
* @Version: 1.0
*/
@Configuration
@ConfigurationProperties("rsa.key")//从配置文件中获取头信息是rsa.key的配置
public class RsaKeyProperties {
private String publicKeyFile;
private String privateKeyFile;
private PublicKey publicKey;
private PrivateKey privateKey;
@PostConstruct//在初始化完成之后执行此方法
public void createRsaKey() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
publicKey=RsaUtil.getPublicKey(publicKeyFile);
privateKey=RsaUtil.getPrivateKey(privateKeyFile);
}
public String getPublicKeyFile() {
return publicKeyFile;
}
public void setPublicKeyFile(String publicKeyFile) {
this.publicKeyFile = publicKeyFile;
}
public String getPrivateKeyFile() {
return privateKeyFile;
}
public PublicKey getPublicKey() {
return publicKey;
}
public void setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
}
public PrivateKey getPrivateKey() {
return privateKey;
}
public void setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
}
public void setPrivateKeyFile(String privateKeyFile) {
this.privateKeyFile = privateKeyFile;
}
}
RsaUtil
package cn.**.util;
import java.io.File;
import java.io.FileOutputStream;
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;
/**
* @ProjectName: springSecuritySeparate
* @Package: cn.**.util
* @Author: huat
* @Date: 2019/12/24 13:29
* @Version: 1.0
*/
public class RsaUtil {
private final static int DEFAULT_KEY_SIZE=2048;
/**
* 根据密文,生成rea公钥和私钥,并写入指定文件
* @param publicKeyFileName 公钥文件路径
* @param privateKeyFileName 私钥文件路径
* @param secret 盐
* @param keySize 大小
*/
public static void generateKey(String publicKeyFileName,String privateKeyFileName,String secret, int keySize) throws NoSuchAlgorithmException, IOException {
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[] publicKeyByte=keyPair.getPublic().getEncoded();
publicKeyByte= Base64.getEncoder().encode(publicKeyByte);
writeFile(publicKeyFileName,publicKeyByte);
//获取公钥并写入
byte[] privateKeyByte=keyPair.getPrivate().getEncoded();
privateKeyByte= Base64.getEncoder().encode(privateKeyByte);
writeFile(privateKeyFileName,privateKeyByte);
}
/**
* 获取秘钥
* @param bytes 秘钥的字节形式
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
bytes=Base64.getDecoder().decode(bytes);
PKCS8EncodedKeySpec spec=new PKCS8EncodedKeySpec(bytes);
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
/**
* 获取公钥
* @param bytes 公钥的字节形式
* @return
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
*/
private static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
bytes=Base64.getDecoder().decode(bytes);
X509EncodedKeySpec spec=new X509EncodedKeySpec(bytes);
KeyFactory keyFactory=KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
}
/**
* 从文件中读取公钥
* @param fileName 公钥保存路径 相对于classpath
* @return 公钥对象
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public static PublicKey getPublicKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
byte[] bytes=readFile(fileName);
return getPublicKey(bytes);
}
/**
* 从文件中读取公钥
* @param fileName 公钥保存路径 相对于classpath
* @return 私钥对象
* @throws InvalidKeySpecException
* @throws NoSuchAlgorithmException
*/
public static PrivateKey getPrivateKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException {
byte[] bytes=readFile(fileName);
return getPrivateKey(bytes);
}
private static byte[] readFile(String fileName) throws IOException {
return Files.readAllBytes(new File(fileName).toPath());
}
private static void writeFile(String fileName,byte[] bytes) throws IOException {
File file=new File(fileName);
if(!file.exists()){
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream( fileName);
fos.write(bytes);
fos.close();
}
}
SpringSercurityConfig
package cn.**.util;
import cn.**.filter.JwtTokenFilter;
import cn.**.filter.JwtVerifyFileter;
import cn.**.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.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;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
/**
* @ProjectName: springbootSecurity
* @Package: cn.cn.**.security.util
* @Author: huat
* @Date: 2019/12/14 8:06
* @Version: 1.0
*/
/**
* 开启security注解支持
*
* @EnableWebSecurity EnableGlobalMethodSecurity 方法级授权
* (securedEnabled=true) 开启@Secured 注解过滤权限
* (jsr250Enabled=true)开启@RolesAllowed 注解过滤权限
* (prePostEnabled=true) 使用表达式时间方法级别的安全性 4个注解可用
* @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)
*/
@Configuration
@EnableWebSecurity
public class SpringSercurityConfig extends WebSecurityConfigurerAdapter {
/* @Autowired
UserService1 userService;*/
@Autowired
UserService userService;
@Autowired
private RsaKeyProperties rsaKeyProperties;
/**
* 将security中加密方式注入到spring容器中
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 解决跨域
* @return
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
//指定允许跨域的请求(*所有):http://wap.ivt.guansichou.com
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH"));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "X-User-Agent", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
/**
* 将账号密码设置在内存当中
*
* @param auth
* @throws Exception
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
//将UserDetailsService放到容器中
.userDetailsService(userService)
//加密方式放入
.passwordEncoder(passwordEncoder());
}
//前后端分离时候使用
@Override
public void configure(HttpSecurity http) throws Exception {
//释放静态资源,指定资源拦截规则,
// 指定自定义认证页面,指定退出认证配置,csrf(跨域伪造请求)配置
http
.csrf()
.disable()//关闭csrf(跨域伪造请求)
.cors()
.and().servletApi().disable()
.requestCache().disable()
.authorizeRequests()
.antMatchers("/index").hasAnyRole("ADMIN")
.antMatchers("/test").hasRole("TEST")
.anyRequest().authenticated()//其他资源需要认证
.and()
.addFilter(new JwtTokenFilter(super.authenticationManager(),rsaKeyProperties))
.addFilter(new JwtVerifyFileter(super.authenticationManager(),rsaKeyProperties))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//禁用session
;
}
}
八、启动类
package cn.**;
import cn.**.util.RsaKeyProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* @ProjectName: springSecurity
* @Package: cn.cn.**
* @Author: huat
* @Date: 2019/12/21 8:38
* @Version: 1.0
*/
@SpringBootApplication
@EnableConfigurationProperties(RsaKeyProperties.class)//加载RsaKeyProperties类,以bean形式注入到spring容器中
public class SpringBootApplicationStart {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplicationStart.class,args);
}
}
九、配置文件
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8
mybatis:
type-aliases-package: cn.**.entity
logging:
level:
cn.**.dao: debug
rsa:
key:
#公钥保存地方
publicKeyFile: E:\test\public.pub
#私钥保存地方
privateKeyFile: E:\test\private
十、拦截器
package cn.**.filter;
import cn.**.dao.UserDao;
import cn.**.entity.AuthorityUser;
import cn.**.entity.Payload;
import cn.**.util.JwtUtil;
import cn.**.util.RsaKeyProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @ProjectName: springbootSecurityJwtOAuth
* @Package: cn.**.filter
* @Author: huat
* @Date: 2019/12/26 13:18
* @Version: 1.0
*/
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Autowired
private UserDao userDao;
@Autowired
private RsaKeyProperties rsaKeyProperties;
//这个方法是在访问接口之前执行的,我们只需要在这里写验证登陆状态的业务逻辑,就可以在用户调用指定接口之前验证登陆状态了
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求头中的信息
String header=request.getHeader("Authorization");
if (null!=header&&header.startsWith("bearer ")){
//获取token
String requestToken=header.replace("bearer ","");
//验证token是否正确
Payload payload= JwtUtil.getInfoFromToken(requestToken,rsaKeyProperties.getPublicKey(), AuthorityUser.class);
AuthorityUser user=payload.getUserInfo();
String token=userDao.getTokenById(user.getId());
if(null!=token&&!"".equals(token)){
if( token.equals(requestToken)){
return true;
}
//如果没有登陆提示用户进行登陆
response.setContentType("application/json;charset=utf-8");
//返回403状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//响应流
PrintWriter out = response.getWriter();
//封装返回数据
Map resultMap = new HashMap();
resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
resultMap.put("data", "您的账号在其他地方已登录");
resultMap.put("msg", "请登录");
//new ObjectMapper().writeValueAsString(resultMap)将map转成json
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
return false;
}
}
//如果没有登陆提示用户进行登陆
response.setContentType("application/json;charset=utf-8");
//返回403状态码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//响应流
PrintWriter out = response.getWriter();
//封装返回数据
Map resultMap = new HashMap();
resultMap.put("code", HttpServletResponse.SC_FORBIDDEN);
resultMap.put("data", "");
resultMap.put("msg", "请登录");
//new ObjectMapper().writeValueAsString(resultMap)将map转成json
out.write(new ObjectMapper().writeValueAsString(resultMap));
out.flush();
out.close();
return false;
}
}
package cn.**.filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ResourceUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @Author: huat
* @Date: 2019/7/26 10:18
* @Version: 1.0
*/
@Configuration
public class WebConfigurer implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
// 这个方法是用来配置静态资源的,比如html,js,css,等等
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//super.addResourceHandlers(registry);
}
// 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns("/**") 表示拦截所有的请求,
//excludePathPatterns("/login", "/register"); 表示除了登陆与注册之外,因为登陆注册不需要登陆也可以访问
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/loginInto", "/login","/login/**","/css/**","/font/**","/img/**","/images/**","/js/**","/lay/**");
//super.addInterceptors(registry); //较新Spring Boot的版本中这里可以直接去掉,否则会报错
}
}