Spring Security Auth2.0的快速实现
**注:**本文只是用于快速实现,没有原理,以及说明
1.加入依赖
<!--security -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
2.yml文件中配置
jwt:
#JWT存储的请求头
tokenHeader: Authorization
#JWT加解密使用的密钥,可修改
secret: erabbit-secret
#JWT超期限的时间(60*60*24)
expiration: 604800
#JWT负载中拿到的开头
tokenHead: Bearer
3.加入配置文件
JwtAuthencationTokenFilter
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.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;
/**
* @author Mr.li
* @program: magipe
* @description jwt登陆授权过滤器
* @create 2021-06-18
**/
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.tokenHeader}")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
String authHeader=httpServletRequest.getHeader(tokenHeader);
//存在token
if (null!=authHeader&&authHeader.startsWith(tokenHead)){
String authToken = authHeader.substring(tokenHead.length());
String username = jwtTokenUtil.getUserNameFromToken(authToken);
//token存在用户名但未登陆
if (null!=username&&null== SecurityContextHolder.getContext().getAuthentication()){
//登陆
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
//验证token是否有效,重新设置用户对象
if (jwtTokenUtil.validateToken(authToken,userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new
UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
filterChain.doFilter(httpServletRequest,httpServletResponse);
}
}
RestAuthorizationEntryPoint
import com.erabbit.erabbit.admin.model.ResultBody;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
import java.io.PrintWriter;
/**
* @author Mr.li
* @program: magipe
* @description 当未登录或者token实现时间访问接口是,自定义返回结果
* @create 2021-06-18
**/
@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
PrintWriter out=httpServletResponse.getWriter();
ResultBody resultBody=ResultBody.failed();
resultBody.code(401);
out.write(new ObjectMapper().writeValueAsString(resultBody));
out.flush();
out.close();
}
}
RestfulAccessDeniedHandler
import com.erabbit.erabbit.admin.model.ResultBody;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
import java.io.PrintWriter;
/**
* @author Mr.li
* @program: magipe
* @description 当接口没有权限时,自定义返回结果
* @create 2021-06-18
**/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json");
PrintWriter out=httpServletResponse.getWriter();
ResultBody resultBody=ResultBody.failed();
resultBody.code(403);
out.write(new ObjectMapper().writeValueAsString(resultBody));
out.flush();
out.close();
}
}
JwtTokenUtil
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @author Mr.li
* @program: magipe
* @description
* @create 2021-06-17
**/
@Component
public class JwtTokenUtil {
private static final String CLAIM_KEY_USERNAME="sub";
private static final String CLAIM_KEY_CREATED="created";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/*
* @Author li
* @Description //根据用户信息生成token
* @Date 2021/6/17/017
* @Param
* @return
**/
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);
}
/*
* @Author li
* @Description //从token中获取登录用户名
* @Date 2021/6/17/017
* @Param [token]
* @return java.lang.String
**/
public String getUserNameFromToken(String token){
String username;
try {
Claims claims=getClaimsFormToken(token);
username=claims.getSubject();
}catch (Exception e){
username=null;
}
return username;
}
/*
* @Author li
* @Description //验证token是否有效
* @Date 2021/6/17/017
* @Param [token, userDetails]
* @return boolean
**/
public boolean validateToken(String token,UserDetails userDetails){
String username=getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/*
* @Author li
* @Description //判断token是否失效
* @Date 2021/6/17/017
* @Param [token]
* @return boolean
**/
private boolean isTokenExpired(String token){
Date expireDate=getExpiredDateFromToken(token);
return expireDate.before(new Date());
}
/*
* @Author li
* @Description //从token中获取过期时间
* @Date 2021/6/17/017
* @Param [token]
* @return java.util.Date
**/
private Date getExpiredDateFromToken(String token){
Claims claims=getClaimsFormToken(token);
return claims.getExpiration();
}
/*
* @Author li
* @Description //判断token是否可以被刷新
* @Date 2021/6/17/017
* @Param [token]
* @return boolean
**/
public boolean canRefresh(String token){
return !isTokenExpired(token);
}
/*
* @Author li
* @Description //刷新token
* @Date 2021/6/17/017
* @Param [token]
* @return java.lang.String
**/
public String refreshToken(String token){
Claims claims=getClaimsFormToken(token);
claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims);
}
/*
* @Author li
* @Description //从token中获取荷载
* @Date 2021/6/17/017
* @Param [token]
* @return io.jsonwebtoken.Claims
**/
private Claims getClaimsFormToken(String token){
Claims claims=null;
try{
claims=Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
e.printStackTrace();
}
return claims;
}
/*
* @Author li
* @Description //根据荷载生成JWT TOKEN
* @Date 2021/6/17/017
* @Param [claims]
* @return java.lang.String
**/
private String generateToken(Map<String,Object> claims){
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512,secret)
.compact();
}
/*
* @Author li
* @Description //生成token失效时间
* @Date 2021/6/17/017
* @Param []
* @return java.util.Date
**/
private Date generateExpirationDate(){
return new Date(System.currentTimeMillis()+expiration*1000);
}
}
SecurityConfig
import com.erabbit.erabbit.admin.entity.User;
import com.erabbit.erabbit.admin.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @author Mr.li
* @program: magipe
* @description
* @create 2021-06-18
**/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserService userService;
@Autowired
private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth)throws Exception{
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//使用jwt不需要csrf
http.csrf()
.disable()
//基于token,不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
//允许登陆访问
.antMatchers("/login/user",
"/user/info").anonymous()
.antMatchers(
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/profile/**"
).permitAll()
.antMatchers("/swagger-ui.html").anonymous()
.antMatchers("/swagger-resources/**").anonymous()
.antMatchers("/webjars/**").anonymous()
.antMatchers("/*/api-docs").anonymous()
//除了上面的都要认证
.anyRequest()
.authenticated()
.and()
.headers()
.cacheControl();
//添加jwt,登陆授权过滤器
http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
http.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthorizationEntryPoint);
}
@Override
@Bean
public UserDetailsService userDetailsService(){
return username->{
User infoByUserName = userService.getInfoByUserName(username);
if (null!=infoByUserName){
return infoByUserName;
}
return null;
};
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthencationTokenFilter jwtAuthencationTokenFilter(){
return new JwtAuthencationTokenFilter();
}
}
注: ResultBody是返回体,可替换,UserService是用户服务
访问时在Headers里面带上key为Authorization,value则是Bearer+token
4.登录的实现
@Autowired
private UserMapper userMapper;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.tokenHead}")
private String tokenHead;
//根据用户名获取用户
@Override
public User getInfoByUserName(String username) {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(User::getUsername,username);
return userMapper.selectOne(queryWrapper);
}
@Override
public TokenInfo login(String userName, String password, HttpServletRequest request) {
//登陆
UserDetails userDetails=userDetailsService.loadUserByUsername(userName);
if (null==userDetails||!passwordEncoder.matches(password,passwordEncoder.encode(userDetails.getPassword()))){
throw new IllegalArgumentException("账号或密码错误");
}
if (!userDetails.isEnabled()){
throw new IllegalArgumentException("该账号已被禁用");
}
//更新登陆
UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(
userDetails,null,userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//生成token
String token=jwtTokenUtil.generateToken(userDetails);
TokenInfo tokenInfo = new TokenInfo();
tokenInfo.setToken(token);
tokenInfo.setTokenHead(tokenHead);
return tokenInfo;
}
返回内容
import lombok.Data;
@Data
public class TokenInfo {
private String token;
private String tokenHead;
}