博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JWT的学习与使用
阅读量:4094 次
发布时间:2019-05-25

本文共 13138 字,大约阅读时间需要 43 分钟。

JWT介绍

JWT是Json Web Token的缩写。它是基于RFC 7519 标准定义的一种可以安全传输的 小巧 和 自包含 的JSON对象。由于数据使用了数字签名,所以是可信任和安全的。

JWT的结构

JWT使用 . 分隔成三部分:

  • Header 头部,包含了两部分:token类型(jwt)和采用的加密算法(HMAC SHA256或RSA)
  • Payload 负载,包含了claim,claim是一些实体(通常指的是用户信息)
  • Signature 签名,由header(base64加密后)、payload (base64加密后)、secret三部分组成
    ⚠️:由于数据声明(Claim)是公开的,千万不要把密码等敏感字段放进去

Header例子:

此json将被Base64Url编码(相等于明文)以形成JWT的第一部分

{  'typ': 'JWT',  'alg': 'HS256'}

Payload例子:

对有效负载进行Base64Url编码,以形成JWT的第二部分

{  "sub": "1234567890",  "name": "John Doe",  "admin": true}

Signature例子

签名用于验证消息在此过程中没有更改,并且对于使用私钥进行签名的令牌,它还可以验证JWT的发送者是它所说的真实身份

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)

JWT的具体使用

1)pom.xml文件中添加相关依赖

io.jsonwebtoken
jjwt
0.9.1

2)JwtAuthenticationTokenFilter类

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {    private final static Logger logger = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);    @Resource    private UserDetailsService userDetailsService;    @Resource    private JwtTokenUtils jwtTokenUtils;    @Resource    private UserService userService;    private String tokenHeader = "Authorization";    private String tokenHead = "Bearer ";    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {        System.out.println("进入到 JWT Filter的doFilterInternal方法中");        //先从url中取token        String authToken = request.getParameter("token");        String authHeader = request.getHeader(this.tokenHeader);        if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(tokenHead)) {            //如果header中存在token,则覆盖掉url中的token            authToken = authHeader.substring(tokenHead.length()); // "Bearer "之后的内容        }        if (StringUtils.isNotBlank(authToken)) {            String username = jwtTokenUtils.getUsernameFromToken(authToken);            logger.info("checking authentication {}", username);            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {                //从已有的user缓存中取了出user信息                User user = userService.findUserByUsername(username);                //检查token是否有效                if (jwtTokenUtils.validateToken(authToken, user)) {                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));                    //设置用户登录状态                    logger.info("authenticated user {}, setting security context",username);                    SecurityContextHolder.getContext().setAuthentication(authentication);                }            }        }        chain.doFilter(request, response);    }}

3)application.yml

config:  jwt:    # 加密密钥    secret: iwqjhda8232bjgh432[cicada-smile]    # token有效时长    expire: 3600

4)JwtTokenUtils类

@Componentpublic class JwtTokenUtils {    private static final String CLAIM_KEY_USERNAME = "sub";    private static final String CLAIM_KEY_ID = "id";    private static final String CLAIM_KEY_CREATED = "created";    private static final String CLAIM_KEY_ROLES = "roles";    @Value("${config.jwt.secret}")    private String secret;    @Value("${config.jwt.expire}")    private int expiration; //过期时长,单位为秒,可以通过配置写入    public String getUsernameFromToken(String token) {        String username;        try {            username =getClaimsFromToken(token).getSubject();        } catch (Exception e) {            username = null;        }        return username;    }    public Date getCreatedDateFromToken(String token) {        Date created;        try {            final Claims claims = getClaimsFromToken(token);            created = new Date((Long) claims.get(CLAIM_KEY_CREATED));        } catch (Exception e) {            created = null;        }        return created;    }    public Date getExpirationDateFromToken(String token) {        Date expiration;        try {            final Claims claims = getClaimsFromToken(token);            expiration = claims.getExpiration();        } catch (Exception e) {            expiration = null;        }        return expiration;    }    private Claims getClaimsFromToken(String token) {        Claims claims;        try {            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();        } catch (Exception e) {            claims = null;        }        return claims;    }    private Date generateExpirationDate() {        return new Date(System.currentTimeMillis() + expiration * 1000);    }    private Boolean isTokenExpired(String token) {        final Date expiration = getExpirationDateFromToken(token);        return expiration.before(new Date());    }    public String generateToken(User userDetails) {        Map
claims = new HashMap<>(); claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); claims.put(CLAIM_KEY_CREATED, new Date()); claims.put(CLAIM_KEY_ID, userDetails.getId()); claims.put(CLAIM_KEY_ROLES, userDetails.getAuthorities()); return generateToken(claims); } public String generateToken(Map
claims) { return Jwts.builder() .setClaims(claims) .setExpiration(generateExpirationDate()) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public Boolean canTokenBeRefreshed(String token) { return !isTokenExpired(token); } public String refreshToken(String token) { String refreshedToken; try { final Claims claims = getClaimsFromToken(token); claims.put(CLAIM_KEY_CREATED, new Date()); refreshedToken = generateToken(claims); } catch (Exception e) { refreshedToken = null; } return refreshedToken; } public Boolean validateToken(String token, UserDetails userDetails) { User user = (User) userDetails; final String username = getUsernameFromToken(token); final Date created = getCreatedDateFromToken(token); return ( username.equals(user.getUsername()) && isTokenExpired(token)==false); }}

5)MySecurityConfig配置文件

@EnableWebSecuritypublic class MySecurityConfig extends WebSecurityConfigurerAdapter {    Logger logger = LoggerFactory.getLogger(getClass());    @Autowired    MyUserDetailService myUserDetailService;    //Control+O 打开重写方法    //定制请求的授权规则    @Override    protected void configure(HttpSecurity http) throws Exception {        System.out.println("进入到 MySecurityConfig的configure 方法中");        //super.configure(http);        /*Spring Security 的默认构造器:        通过调用authorizeRequests()和 anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证        http            .authorizeRequests()                .anyRequest().authenticated()                .and()            .formLogin().and()            .httpBasic(); //弹出一个输入用户名、密码的登录框                         “/shop/hello” 和 “/shop/order” 这两个路径必须进过认证,并且 “/shop/order” 必须是 post 请求的方式.             对于其他的请求,我们都是 .anyRequest().permitAll() ;都放行.             http.authorizeRequests()                .antMatchers("/shop/hello").authenticated()                .antMatchers(HttpMethod.POST,"/shop/order").authenticated()                .anyRequest().permitAll();             antMatchers()方法所使用的路径可能会包括Ant风格的通配符,而regexMatchers()方法则能够接受正则表达式来定义请求路径。         */        // 基于token,所以不需要session        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);        // /**代表所有的请求        http.authorizeRequests()//方法表示开启了认证规则配置;定义哪些url需要保护,哪些url不需要保护;                .antMatchers("/api/**").permitAll()//定义不需要认证就可以访问//                .antMatchers("/session/**").permitAll()//定义不需要认证就可以访问//                .antMatchers("/component/**").hasAuthority("ROLE_ADMIN")                .antMatchers("/home/**").hasAnyAuthority("ROLE_DEV_APPLICATION","ROLE_ADMIN","ROLE_DEV_TERMINAL_IOS")                .anyRequest().authenticated();其他的路径都是登录后即可访问//                .and().formLogin().loginPage("/")////                //在successHandler中,使用response返回登录成功的json即可,切记不可以使用defaultSuccessUrl,defaultSuccessUrl是只登录成功后重定向的页面,failureHandler也是由于相同的原因不使用failureUrl。//                .loginProcessingUrl("/login").successHandler(//                        new AuthenticationSuccessHandler(){//                            @Override//                            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {//                                System.out.println(authentication.getDetails());//                                httpServletResponse.setContentType("application/json;charset=utf-8");//                                PrintWriter out = httpServletResponse.getWriter();//                                out.write("{\"status\":\"success\",\"msg\":\"登录成功\"}");//                                out.flush();//                                out.close();//                            }//                }).failureHandler(//                        new AuthenticationFailureHandler() {//                            @Override//                            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {//                                httpServletResponse.setContentType("application/json;charset=utf-8");//                                PrintWriter out = httpServletResponse.getWriter();//                                out.write("{\"status\":\"failed\",\"msg\":\"登录失败\"}");//                                out.flush();//                                out.close();//                            }//                });                //http.logout()开启自动配置的注销功能                //1) 访问/logout 表示用户注销,清空session                //2) 注销成功会返回/login?logout 页面                //3) logoutSuccessfulUrl 改变2)的设置                http.logout().logoutSuccessUrl("/login");                http.sessionManagement().invalidSessionUrl("/login");                http.rememberMe().rememberMeParameter("remember");//                .usernameParameter("username").passwordParameter("password").defaultSuccessUrl("/");定义当需要用户登录时候,转到的登录页面//                http.headers().frameOptions().disable();//                .antMatchers("/level2/**").hasRole("VIP2")//                .antMatchers("/level3/**").hasRole("VIP3");        //开启自动配置的登录功能。如果没有登录,没有权限就会来到登录页面        //1:/login来到登录页        //2:重定向/login?error表示登录失败        //3:更多详细规定        //http.formLogin().defaultSuccessUrl("/user/login.html");        /* iframe */	    http.headers().frameOptions().sameOrigin(); // 运行同一个域名中的任何请求        http.csrf().disable(); // 默认是启用的,需要禁用CSRF保护(当不使用cookie时可以禁用csrf)        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);        // 禁用缓存        http.headers().cacheControl();    }    //定制请求的认证规则    @Autowired    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {        System.out.println("进入到 configureGlobal 方法中");        System.out.println("auth.userDetailsService(myUserDetailService):" + auth.userDetailsService(myUserDetailService));    //1)获取内存中的用户名和密码//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())                .withUser("1").password(new BCryptPasswordEncoder().encode("1")).roles("USER");//                .withUser("1").password(new BCryptPasswordEncoder().encode("1")).authorities("ROLE_TEST");//        auth.authenticationProvider(authenticationProvider());//        auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());    //2)获取数据库中的用户名和密码        auth.userDetailsService(myUserDetailService).passwordEncoder(new PasswordEncoder() {            @Override            public String encode(CharSequence charSequence) {                return charSequence.toString();            }            @Override            public boolean matches(CharSequence charSequence, String s) {                return s.equals(charSequence.toString());            }        });    }    /*     通过AuthenticationProvider方式获取     */    @Bean    public DaoAuthenticationProvider authenticationProvider() {        System.out.println("进入到 authenticationProvider 方法中");        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();        authProvider.setUserDetailsService(myUserDetailService);        authProvider.setPasswordEncoder(passwordEncoder());        return authProvider;    }    /**     * 密码生成策略     * @return     */    @Bean    public BCryptPasswordEncoder passwordEncoder() {        System.out.println("进入到 passwordEncoder 方法中");        return new BCryptPasswordEncoder();    }    @Bean    public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {        System.out.println("进入到 authenticationTokenFilterBean 方法中");        return new JwtAuthenticationTokenFilter();    }}

转载地址:http://zrtii.baihongyu.com/

你可能感兴趣的文章
解析Java中的String对象的数据类型
查看>>
验证用户名,AJAX步骤示例
查看>>
AJAX w3school 以及IE、Firefox、Chrome 的JS代码兼容注意事项
查看>>
Js事件绑定时,函数名加括号和不加括号有什么区别
查看>>
套接字
查看>>
PrintWriter out = response.getWriter()
查看>>
AJAX 示例:xml数据类型返回
查看>>
AJAX实现步骤,XMLHttpRequest对象的方法
查看>>
jQuery ajax - ajax() 方法
查看>>
table结构本身有tbody
查看>>
jQuery 遍历 - each() 方法
查看>>
jQuery 选择器--w3school
查看>>
练习jQuery 。table切换
查看>>
正则表达式
查看>>
AJAX ,在jsp处理 action返回的不同数据类型
查看>>
SVN设置笔记1,CentOS装SVN
查看>>
SVN设置笔记2,MyEclipse装SVN插件
查看>>
SVN设置笔记3,项目使用SVN
查看>>
TestSingletonDemo:
查看>>
使用Hibernate步骤:(一)
查看>>