Spring Security Auth2.0的快速实现

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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值