1. 程式人生 > >SpringBoot+SpringSecurity+JWT實RESTfulAPI許可權控制

SpringBoot+SpringSecurity+JWT實RESTfulAPI許可權控制

工具類JwtTokenUtil:

package org.security.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.security.entity.SysUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
public class JwtTokenUtil implements Serializable {

    private static final long serialVersionUID = -3301605591108950415L;

    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;

    public String getUsernameFromToken(String token) {
        String username;
        try {
            final Claims claims = getClaimsFromToken(token);
            username = claims.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());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }

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

    String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && !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) {
        SysUser user = (SysUser) userDetails;
        final String username = getUsernameFromToken(token);
        final Date created = getCreatedDateFromToken(token);
//        final Date expiration = getExpirationDateFromToken(token);
        return (
                username.equals(user.getUsername())
                        && !isTokenExpired(token)
                        && !isCreatedBeforeLastPasswordReset(created, user.getLastPasswordResetDate()));
    }
}
dao介面:
package org.security.dao;
import org.security.entity.SysUser;
public interface AuthService {
    SysUser register(SysUser userToAdd);
    String login(String username, String password);
    String refresh(String oldToken);
}
dao介面實現類(service業務類):
package org.security.service;
import org.security.dao.AuthService;
import org.security.dao.SysUserRepository;
import org.security.entity.SysUser;
import org.security.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Date;

import static java.util.Arrays.asList;

@Service
public class AuthServiceImpl implements AuthService {

    private AuthenticationManager authenticationManager;
    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private SysUserRepository userRepository;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    public AuthServiceImpl(
            AuthenticationManager authenticationManager,
            UserDetailsService userDetailsService,
            JwtTokenUtil jwtTokenUtil,
            SysUserRepository userRepository) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.userRepository = userRepository;
    }

    @Override
    public SysUser register(SysUser userToAdd) {
        final String username = userToAdd.getUsername();
        if(userRepository.findByUsername(username)!=null) {
            return null;
        }
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        final String rawPassword = userToAdd.getPassword();
        userToAdd.setPassword(encoder.encode(rawPassword));
        userToAdd.setLastPasswordResetDate(new Date());
//        userToAdd.setRoles(asList("ROLE_USER"));
        return userRepository.save(userToAdd);
    }

    @Override
    public String login(String username, String password) {
        UsernamePasswordAuthenticationToken upToken = new UsernamePasswordAuthenticationToken(username, password);
        // Perform the security
        final Authentication authentication = authenticationManager.authenticate(upToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // Reload password post-security so we can generate token
        final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        final String token = jwtTokenUtil.generateToken(userDetails);
        return token;
    }

    @Override
    public String refresh(String oldToken) {
        final String token = oldToken.substring(tokenHead.length());
        String username = jwtTokenUtil.getUsernameFromToken(token);
        SysUser user = (SysUser) userDetailsService.loadUserByUsername(username);
        if (jwtTokenUtil.canTokenBeRefreshed(token, user.getLastPasswordResetDate())){
            return jwtTokenUtil.refreshToken(token);
        }
        return null;
    }
}
controller控制類:
package org.security.controller;
import org.security.dao.AuthService;
import org.security.entity.SysUser;
import org.security.service.JwtAuthenticationRequest;
import org.security.service.JwtAuthenticationResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@RestController
public class AuthController {
    @Value("${jwt.header}")
    private String tokenHeader;

    @Autowired
    private AuthService authService;

    @RequestMapping(value = "/auth/login", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(
            String username,String password
    ) throws AuthenticationException{
        //  @RequestBody JwtAuthenticationRequest authenticationRequest
        final String token = authService.login(username,password);

        // Return the token
        return ResponseEntity.ok(new JwtAuthenticationResponse(token));
    }

    @RequestMapping(value = "/auth/refresh", method = RequestMethod.GET)
    public ResponseEntity<?> refreshAndGetAuthenticationToken(
            HttpServletRequest request) throws AuthenticationException{
        String token = request.getHeader(tokenHeader);
        String refreshedToken = authService.refresh(token);
        if(refreshedToken == null) {
            return ResponseEntity.badRequest().body(null);
        } else {
            return ResponseEntity.ok(new JwtAuthenticationResponse(refreshedToken));
        }
    }

    @RequestMapping(value = "${jwt.route.authentication.register}", method = RequestMethod.POST)
    public SysUser register(@RequestBody SysUser addedUser) throws AuthenticationException{
        return authService.register(addedUser);
    }
}

接在來就是最重要的filter類:

package org.security.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.security.util.JwtTokenUtil;
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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${jwt.header}")
    private String tokenHeader;

    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {
        String authHeader = request.getHeader(this.tokenHeader);
        if (authHeader != null && authHeader.startsWith(tokenHead)) {
            final String authToken = authHeader.substring(tokenHead.length()); // The part after "Bearer "
            String username = jwtTokenUtil.getUsernameFromToken(authToken);
            logger.info("checking authentication " + username);
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                	if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                                request));
                        logger.info("authenticated user " + username + ", setting security context");
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }
            }
        }
        chain.doFilter(request, response);
    }
}
將security中配置檔案註解去掉,去掉之後如下所示:
package org.security.config;

import org.security.service.CustomUserService;
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.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    protected UserDetailsService customUserService() {
        return new CustomUserService();
    }

    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                // 設定UserDetailsService
                .userDetailsService(customUserService())
                // 使用BCrypt進行密碼的hash
                .passwordEncoder(passwordEncoder());
    }

    // 裝載BCrypt密碼編碼器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        return new JwtAuthenticationTokenFilter();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserService());
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                // 由於使用的是JWT,我們這裡不需要csrf
                .csrf().disable()
                // 基於token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 允許對於網站靜態資源的無授權訪問
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                // 對於獲取token的rest api要允許匿名訪問
                .antMatchers("/auth/**").permitAll()
                .antMatchers("/admin/**").hasIpAddress("127.0.0.1")
                .antMatchers("/admin/**").access("hasAuthority('ROLE_ADMIN')")
//                .anyRequest().authenticated().and().formLogin().loginPage("/login")
//                .failureUrl("/login?error").permitAll().and().logout().permitAll();
        // 除上面外的所有請求全部需要鑑權認證
				.anyRequest().authenticated();
        // 新增JWT filter
        httpSecurity
                .addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        // 禁用快取
        httpSecurity.headers().cacheControl();
    }
}
到這邊jwt整合也介紹了,現在我們就可以postman來進行測試。

測試結果如下所示:
生成token:


重新整理token:


不帶token訪問介面:


帶token但是許可權不夠:
帶token而且許可權也夠:


到這裡關於 SpringBoot+SpringSecurity+JWT就基本結束了,這篇部落格我重點是將如何通過程式碼來實現這個功能,關於程式碼本身沒有很細緻的介紹。關於程式碼功能的介紹,例如怎麼通過註解配置許可權控制,如何通過配置來進行許可權控制,token更新和驗證等等的細節,我會在我這篇部落格的教學視訊中介紹,視訊地址:點選開啟連結

GitHub地址:點選開啟連結

如果大家對文章有什麼問題或者疑意之類的,可以加我訂閱號在上面留言,訂閱號上面我會定期更新最新部落格。如果嫌麻煩可以直接加我wechat:lzqcode

相關推薦

SpringBoot+SpringSecurity+JWTRESTfulAPI許可權控制

工具類JwtTokenUtil:package org.security.util; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import or

SpringBoot--- SpringSecurity進行登出,許可權控制

## SpringBoot--- SpringSecurity進行登出,許可權控制 ### 環境 IDEA :2020.1 Maven:3.5.6 SpringBoot: **2.0.9** (與此前整合的版本2.3.3 不同,版本適配問題,為配合使用降級) ### 1、登出 這裡也有一個前提問題

springboot+shiro+mybatis實現角色許可權控制

背景 spring+spirngmvc+shiro的整合已經有很多了,之前的專案中也用過,但是最近想在springboot中使用shiro這樣,其他專案需要的時候只需要把它依賴進來就可以直接使用,至於shiro的原理其他的blog都有很多介紹。這裡只講幾個重點

Springboot+SpringMVC+Myabtis整合shiro許可權控制

最近也被這個難題搞的我頭都大了額。寫下此篇文章獻給自己和廣大朋友。如果有不懂的地方可以加企鵝號詢問哦。 企鵝號:2054861587,不一定會時時在,但如果有空的話就會給你回答 maven依賴: <dependency> <groupId>

SpringBoot 結合SpringSecurity+Jwt實現許可權認證

首先建立RBAC 許可權系統表 /* MySQL Backup Database: test Backup Time: 2018-09-12 11:53:20 */ SET FOREIGN_KEY_CHECKS=0; DROP TABLE IF EXISTS `test`

springboot+springsecurity+cas實現sso,並開啟註解方法級別的許可權控制

springsecurity整合cas springsecurity的springsecurityfilterchian中有一個cas的攔截器位置,因此可以通過它把cas整合進springsecurity中 cas負責認證,security通過userDetails負責載入許可權,通過認證管理器

SpringBoot整合Springsecurity實現資料庫登入以及許可權控制

我們今天使用SpringBoot來整合SpringSecurity,來吧,不多BB 首先呢,是一個SpringBoot 專案,連線資料庫,這裡我使用的是mybaties.mysql, 下面是資料庫的表 DROP TABLE IF EXISTS `xy_role`; CR

成長記錄貼之springboot+shiro(二) {完成一個完整的許可權控制,詳細步驟}

       近一個月比較忙,公司接了一個新專案,領導要求用shiro進行安全管理,而且全公司只有我一個java,從專案搭建到具體介面全是一個人再弄。不過剛好前段時間大概學習了一下shiro的使用,還算順利。       &n

springboot使用Spring Security+OAuth2做許可權控制

文章來源:http://lxgandlz.cn/404.html     前面有一篇文章Spring+Spring Security+OAuth2實現REST API許可權控制,講了Spring+Spring Security+OAuth2來實現REST API許可權

springboot配置shiro許可權管理,網搜搜採集網站許可權控制程式碼

import outshine.shiro.authc.AccountSubjectFactory; import outshine.shiro.filter.AuthenticatedFilter; import outshine.shiro.realm.AccountRealm; import or

SpringBoot2.0整合SpringSecurity並實現驗證碼和許可權控制

使用SpringBoot2.0 整合SpringSecurity加入kaptcha驗證碼,使用redis實現session共享,請看原始碼,附上資料庫指令碼,絕對可以跑起來,原始碼地址為 1、pom檔案 <?xml version="1.0" encoding=

SpringbootSpringboot + spring boot admin 監控 spring security許可權控制

Springboot admin 很好的提供了對Springboot的監控,但是不建議直接將admin整合到已有的專案中。於是我另起一個專案,考慮到不能讓所有人都能看到這些資料了,於是引入了spring security。   本次使用的是spring-boot-admin-s

SpringBoot+shiro整合學習之登入認證和許可權控制

學習任務目標 使用者必須要登陸之後才能訪問定義連結,否則跳轉到登入頁面。 對連結進行許可權控制,只有噹噹前登入使用者有這個連結訪問許可權才可以訪問,否則跳轉到指定頁面。 輸入錯誤密碼使用者名稱或則使用者被設定為靜止登入,返回相應json串資訊 匯

springboot整合security實現基於url的許可權控制

  許可權控制基本上是任何一個web專案都要有的,為此spring為我們提供security模組來實現許可權控制,網上找了很多資料,但是提供的demo程式碼都不能完全滿足我的需求,因此自己整理了一版。   在上程式碼之前,大家需要理解兩個過程:認證和授權   使用者登陸,會被AuthenticationPro

springboot整合shiro 實現許可權控制

shiro apache shiro 是一個輕量級的身份驗證與授權框架,與spring security 相比較,簡單易用,靈活性高,springboot本身是提供了對security的支援,畢竟是自家的東西。springboot暫時沒有整合shiro,這得自

SpringBoot + SpringSecurity 控制授權

授權簡介 一般的人會認為,不同的角色登入進同一個系統,根據角色許可權的不同,看到的選單不同就是控制授權。其實並不是的,選單的是否顯示只是前端互動上的一個設計而已,真正需要授權的地方的介面的訪問。 普通的系統通常會有兩個端,一個是給使用者用的業務系統(比如購

SpringBoot學習筆記(六):SpringBoot實現Shiro登入控制許可權控制

登入模組:在登入時必須呼叫 授權模組:不是一登入就調動,而是當角色許可權控制時才會呼叫 登入控制 環境搭建在上一篇。 資料庫表 表名:role 欄位:id rolename 表名:user 欄位:id username sex roleid 在程式碼中簡歷資料庫表對應的實

前後端分離 SpringBoot + SpringSecurity 許可權解決方案

一、前言 而公司這邊都是前後端分離鮮明的,前端不要接觸過多的業務邏輯,都由後端解決,基本思路是這樣的: 服務端通過 JSON字串,告訴前端使用者有沒有登入、認證,前端根據這些提示跳轉對應的登入頁、認證頁等。 二、程式碼 下面給個示例,該自上述的之

SpringBoot中使用Spring Security實現許可權控制

Spring Security,這是一個專門針對基於Spring的專案的安全框架,它主要是利用了AOP來實現的。以前在Spring框架中使用Spring Security需要我們進行大量的XML配置,但是,Spring Boot針對Spring Security

springBoot+springSecurity 資料庫動態管理使用者、角色、許可權(二)

序: 本文使用springboot+mybatis+SpringSecurity 實現資料庫動態的管理使用者、角色、許可權管理 本文細分角色和許可權,並將使用者、角色、許可權和資源均採用資料庫儲存,並且自定義濾器,代替原有的FilterSecurityInt