【JAVA】jwt实战

【JAVA】jwt


jwt用于登陆验证后token发放,后续请求头中携带token进行鉴权操作

SecurityConfig.java

@Configuration
@EnableWebSecurity // 开启Security
@EnableGlobalMethodSecurity(prePostEnabled = true) // 保证post之前的注解可以使用
public class  SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    JwtAuthorizationTokenFilter authenticationTokenFilter;

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                .antMatchers("/templates/**", "/images/**", "/scripts/**", "/styles/**","/serveDataLogMessage").permitAll()
                .mvcMatchers("/", "/home", "/basics", "/theme", "/application**", "/catalog/**", "/public/**", "/uib/**").permitAll()
                .antMatchers("/ca/getBase64String", "/ca/challengeTicket", "/ca/download").permitAll()
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/developer/**").hasRole("DEVELOPER")
                .antMatchers("/manager/**").hasRole("ADMIN")
                .antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable();

        // 添加JWT filter
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

JwtAuthorizationTokenFilter.java

@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        // public中获取用户信息的接口,需要解析token
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isNotEmpty(authorization) && authorization.startsWith("Bearer ") && authorization.length() > 10) {
            setUserContext(request, response);
            chain.doFilter(request, response);
            return;
        }

        String servletPath = request.getServletPath();
        // 不需要token直接放行的url
        if ("/ca/getBase64String".equals(request.getServletPath())) {
            chain.doFilter(request, response);
            return;
        }
        if ("/ca/challengeTicket".equals(request.getServletPath())) {
            chain.doFilter(request, response);
            return;
        }
        if ("/ca/download".equals(request.getServletPath())) {
            chain.doFilter(request, response);
            return;
        }
        if (servletPath.startsWith("/templates")
                || servletPath.startsWith("/images")
                || servletPath.startsWith("/scripts")
                || servletPath.startsWith("/styles")
                || servletPath.startsWith("/serveDataLogMessage")
                || servletPath.startsWith("/home")
                || servletPath.startsWith("/basics")
                || servletPath.startsWith("/theme")
                || servletPath.startsWith("/application")
                || servletPath.startsWith("/catalog")
                || servletPath.startsWith("/public")
                || servletPath.startsWith("/uib")
        ) {
            chain.doFilter(request, response);
            return;
        }

        // 无token
        if (StringUtils.isEmpty(authorization) || "Bearer".equals(authorization.trim())) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "token不存在");
            return;
        } else {
            // token无效
            boolean checkToken = JwtUtils.checkToken(authorization.substring(7));
            if (!checkToken) {
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "token无效");
                return;
            }
        }

        if (StringUtils.isNotEmpty(authorization) && authorization.startsWith("Bearer ")) {
            setUserContext(request, response);
        }

        chain.doFilter(request, response);
    }

    private void setUserContext(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String jwtToken = request.getHeader("Authorization").substring(7);
        String secret = PropertiesUtils.getConfigByYml("jwt.secret");
        // 获取载荷payload
        Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtToken).getBody();
        ObjectMapper objectMapper = new ObjectMapper();
        String payload = objectMapper.writeValueAsString(claims);
        JSONObject jsonObject = JSONObject.fromObject(payload);
        String userid = (String) jsonObject.get("appAccount");

        if (userid != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            // 通过数据库获取用户角色
            User user = userService.getById(userid);
            List<Role> roles = new ArrayList<>();
            if (user != null) {
                // 获取角色和部门
//                    roles = roleService.getRolesByUserid(user.getId());
                String[] rolesstr = user.getUserRole().split(",");
                for(String r:rolesstr){
                    if(!StringUtils.isEmpty(r)){
                        Role role=new Role();
                        role.setId(r);
                        roles.add(role);
                    }
                }
                user.setRoles(roles);
            } else {
                // ca用户不在本地系统数据库中
                response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "用户" + userid + "不存在");
                return;
            }

            // 设置角色权限
            List<GrantedAuthority> authorityList = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(roles)) {
                for (Role role : roles) {
                    if (role.getId().startsWith("ROLE_")) {
                        authorityList.add(new SimpleGrantedAuthority(role.getId()));
                    } else {
                        authorityList.add(new SimpleGrantedAuthority("ROLE_" + role.getId()));
                    }
                }
            }

            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userid, null, authorityList);

            // 用户信息方法spring上下文中
            SecurityContextHolder.getContext().setAuthentication(authentication);

        }
    }
}

JwtAuthenticationEntryPoint.java

@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException)
            throws IOException, ServletException {

        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "认证失败");
    }
}

JwtUtils.java

import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

public class JwtUtils {

//    public static final long EXPIRE = 1000 * 60 * 10; // token有效期10分钟

    /**
     * 根据用户id和昵称生成token
     *
     * @param appAccount 用户名
     * @return JWT规则生成的token
     */
    public static String getJwtToken(String appAccount) {

        String secret = PropertiesUtils.getConfigByYml("jwt.secret");
        int EXPIRE = Integer.parseInt(PropertiesUtils.getConfigByYml("jwt.expiration"));

        String JwtToken = Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                .setSubject("bigData")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim("appAccount", appAccount)
                // HS256算法实际上就是MD5加盐值,此时SECRET就代表盐值
                .signWith(SignatureAlgorithm.HS256, secret)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     *
     * @param jwtToken token字符串
     * @return 如果token有效返回true,否则返回false
     */
    public static boolean checkToken(String jwtToken) {
        String secret = PropertiesUtils.getConfigByYml("jwt.secret");

        if (StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     *
     * @param request Http请求对象
     * @return 如果token有效返回true,否则返回false
     */
    public static boolean checkToken(HttpServletRequest request) {
        String secret = PropertiesUtils.getConfigByYml("jwt.secret");

        try {
            // 从http请求头中获取token字符串
            String jwtToken = request.getHeader("token");
            if (StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     *
     * @param request Http请求对象
     * @return 解析token后获得的用户id
     */
    public static String getAppAccountByJwtToken(HttpServletRequest request) {
        String secret = PropertiesUtils.getConfigByYml("jwt.secret");
        String jwtToken = request.getHeader("Authorization");
        if (StringUtils.isEmpty(jwtToken)) return "";
        if (StringUtils.isNotEmpty(jwtToken) && jwtToken.startsWith("Bearer ")) {
            jwtToken = jwtToken.substring(7);
        }
        try {
            // 获取载荷payload
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtToken).getBody();
            ObjectMapper objectMapper = new ObjectMapper();
            String payload = objectMapper.writeValueAsString(claims);
            JSONObject jsonObject = JSONObject.fromObject(payload);
            String appAccount = (String) jsonObject.get("appAccount");
            return appAccount;
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 刷新token过期时间
     *
     * @param jwtToken 原始token
     * @return 新token
     */
    public static String refreshToken(String jwtToken) {

        if (StringUtils.isEmpty(jwtToken)) return null;

        String newToken = "";
        try {
            String secret = PropertiesUtils.getConfigByYml("jwt.secret");
            int EXPIRE = Integer.parseInt(PropertiesUtils.getConfigByYml("jwt.expiration"));
            // 获取载荷payload
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwtToken).getBody();
            ObjectMapper objectMapper = new ObjectMapper();
            String payload = objectMapper.writeValueAsString(claims);
            JSONObject jsonObject = JSONObject.fromObject(payload);

            // iat: jwt的签发时间
            int iat = (int) jsonObject.get("iat");
            // 当前时间-签发时间 < 过期时间的一半,不用重新发
//            if ((System.currentTimeMillis() - iat * 1000L) < (EXPIRE / 2)) {
//                return jwtToken;
//            }
            // exp: jwt的过期时间
            // 重置过期时间
            jsonObject.put("exp", ((System.currentTimeMillis() + EXPIRE) / 1000));
            // 重置签发时间
            jsonObject.put("iat", ((System.currentTimeMillis()) / 1000));

            String newPayload = jsonObject.toString();
            newToken = Jwts.builder()
                    .setHeaderParam("typ", "JWT")
                    .setHeaderParam("alg", "HS256")
                    .setPayload(newPayload)
                    .signWith(SignatureAlgorithm.HS256, secret)
                    .compact();

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return newToken;
    }

    public static String refreshToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        if (token != null && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }
        return refreshToken(token);
    }
}

PropertiesUtils.java

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class PropertiesUtils {
    private static Logger logger = LoggerFactory.getLogger(PropertiesUtils.class);
    private static Map<String,Properties> propertiesMap = new HashMap<String,Properties>();
    /**
     * 默认yml后缀文件名称
     */
    private final static String DEFAULT_YML_NAME = "application.yml";
    /**
     * 默认properties后缀文件名称
     */
    private final static String DEFAULT_PROPERTIES_NAME = "application.properties";

    /**
     * Yaml 文件转换
     * @param yamlFileName
     */
    private static void setYamlPropertiesToMap(String yamlFileName){
        if (StringUtils.isBlank(yamlFileName)) {
            throw new RuntimeException(yamlFileName + "不能为空");
        }
        try {
            YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
            yamlFactory.setResources(new ClassPathResource(yamlFileName));
            propertiesMap.put(yamlFileName,yamlFactory.getObject());
        } catch (Exception e) {
            logger.error("转换异常:",e);
        }
    }

    /**
     * Properties 文件转换
     * @param propertiesFileName
     */
    private static void setPropertiesToMap(String propertiesFileName){
        if (StringUtils.isBlank(propertiesFileName)) {
            throw new RuntimeException(propertiesFileName + "不能为空");
        }
        try {
            propertiesMap.put(propertiesFileName, PropertiesLoaderUtils.loadAllProperties(propertiesFileName)); ;
        } catch (IOException e) {
            logger.error("IO异常:",e);
        }
    }
    /**
     * 获取yml配置文件中的值
     *
     * @param key 需要获取的键名
     * @return 对应的值
     */
    public static String getConfigByYml(String key) {
        return getConfigByYml(key,DEFAULT_YML_NAME);
    }

    /**
     * 获取yml配置文件中的值
     *
     * @param key        需要获取的键名
     * @param configName 自定义配置文件yml后缀的名称
     * @return 对应的值
     */
    public static String getConfigByYml(String key, String configName) {
        if(!propertiesMap.containsKey(configName)){
            setYamlPropertiesToMap(configName);
        }
        return propertiesMap.get(configName).getProperty(key);
    }

    /**
     * 获取properties配置文件中的值
     *
     * @param key 需要获取的键
     * @return 对应的值
     */
    public static String getConfigByProperties(String key) {
        return getConfigByProperties(key,DEFAULT_PROPERTIES_NAME);
    }

    /**
     * 获取properties配置文件中的值
     *
     * @param key        需要获取的键
     * @param configName properties配置文件的名称
     * @return 对应的值
     */
    public static String getConfigByProperties(String key, String configName) {
        if(!propertiesMap.containsKey(configName)){
            setPropertiesToMap(configName);
        }
        return propertiesMap.get(configName).getProperty(key);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值