개요
우리 모두 알다시피, 권한 확인을 위해 JWT를 사용합니다. 세션과 비교할 때 세션의 장점은 세션에 많은 양의 서버 메모리가 필요하며 여러 서버를 사용하면 공유 세션 문제가 포함되며 휴대 전화와 같은 모바일 터미널에 액세스 할 때 더 문제가됩니다.
JWT는 서버에 저장 될 필요가 없으며 서버 리소스 (즉, SANTELESS)를 차지하지 않습니다. 사용자가 로그인하면 권한이 필요한 요청에 액세스 할 때 (일반적으로 HTTP 요청 헤더에 설정) 토큰에 첨부됩니다. JWT는 여러 서버를 공유하는 데 문제가 없으며 휴대 전화에서 모바일 액세스 문제가 없습니다. 보안을 향상시키기 위해 토큰을 사용자의 IP 주소에 묶을 수 있습니다.
프론트 엔드 프로세스
사용자는 Ajax를 통해 로그인하여 토큰을 얻었습니다
그 후 액세스 권한이 필요할 때 액세스 할 수있는 토큰을 연결하십시오.
<! doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <title> title </title> <script src = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"> <clatt "> application ="/javascript ". "";; 함수 login () {$ .post ( "http : // localhost : 8080/auth/login", {username : $ ( "#username"). val (), password : $ ( "#password"). val ()}, function (data) {console.log (data); header = data; "get": "http : // localhost : 8080/userpage", beforeSend : request.setRequestHeader ( "Authorization", success); } </script> </head> <boddset> <legend> 로그인 </legend> <label> username </label> <입력 유형 = "text"id = "username"> <label> password </label> <입력 유형 = "text"id = "post"> <input type = "button"onclick = <login () ""vally "> </fileddeN"> id = "touserpagebtn"onclick = "touserpagebtn ()"> access userpage </button> </body> </html>백엔드 프로세스 (Spring Boot + Spring Security + JJWT)
아이디어 :
사용자 엔티티 클래스를 작성하고 데이터를 삽입하십시오.
사용자 (사용자) 엔티티 클래스
@data @entitypublic class user {@id @generatedValue private int id; 개인 문자열 이름; 개인 문자열 비밀번호; @manytomany (cascade = {cascadetype.refresh}, fetch = fetchtype.eger) @jointable (name = "user_role", joincolumns = {@joincolumn (이름 = "uid", 참조 Columnname = "id")}, inversejoincolumn = @joincol (name = "rid referenceColumnName = "id")}) 개인 목록 <Brole> 역할;} 역할 (허가) 엔티티 클래스
@data @entitypublic 클래스 역할 {@id @generatedValue private int id; 개인 문자열 이름; @manytomany (mappedby = "역할") 개인 목록 <user> 사용자;} 데이터 삽입
사용자 테이블
| ID | 이름 | 비밀번호 |
|---|---|---|
| 1 | Linyuan | 123 |
역할 테이블
| ID | 이름 |
|---|---|
| 1 | 사용자 |
user_role 테이블
| uid | 제거하다 |
|---|---|
| 1 | 1 |
DAO 레이어 인터페이스, 사용자 이름을 통해 데이터를 얻고 Java8 값으로 선택적인 객체를 반환합니다.
공개 인터페이스 userrepository 확장 리포지토리 <User, Integer> {옵션 <user> findbyName (문자열 이름);} 프론트 엔드로 데이터 전송을 위해 LoginDTO를 작성하십시오
@datapublic class logindto는 시리얼이즈 가능한 {@notblank (message = "사용자 이름이 비어있을 수 없다") 개인 문자열 사용자 이름을 구현합니다. @NotBlank (Message = "비밀번호가 비어있을 수 없음") 개인 문자열 비밀번호;} 토큰 생성 도구를 작성하고 JJWT 라이브러리를 사용하여 작성하십시오. 총 세 가지 방법이 있습니다 : 토큰을 생성 (문자열 반환), 토큰을 구문 분석 (인증 인증 객체 반환), 토큰 (부울 값 반환)을 확인하십시오.
@ComponentPublic Class JWTTOKNUTILS {Private Final Logger log = loggerFactory.getLogger (jwttokenutils.class); 개인 정적 최종 문자열 당국 _key = "Auth"; 개인 문자열 비밀; // 주요 개인 장거리 TokenValidityInmilliseconds 서명; // 만료 날짜 비공개 긴 토큰 볼 리노 힐 킬리 몬드 우르 메아버; // (나를 기억하십시오) 만료 날짜 @PostConstruct public void init () {this.secretkey = "linyuanmima"; int secondIn1day = 1000 * 60 * 60 * 24; this.tokenvalidityInmilliseconds = secondIn1day * 2L; this.tokenvalidymillisecondsforremembeme = secondin1day * 7l; } 개인 최종 정적 긴 만료 시간 = 432_000_000; // 토큰 공개 문자열 createToken 만들기 (인증 인증, 부울 기억 me) {문자열 당국 = 인증. long now = (new date ()). gettime (); // 현재 타임 스탬프 날짜 유효성을 가져옵니다. // 저장 만료 시간 if (rememberme) {agividity = new 날짜 (지금 + this.tokenvalidityInmilliseconds); } else {validity = 새 날짜 (지금 + this.TokenValidityMilliseCondSforRemembeMe); } return jwts.builder () // 토큰 토큰 생성 Token.setSubject (authentication.getName ()) // set user-oriented.claim (userities_key, 권한) // 허가 속성 추가. } // 사용자 권한 가져 오기 공개 인증 getAuthentication (String Token) {System.out.println ( "Token :"+Token); 클레임 주장 = jwts.parser () // parse token 's payload .setSigningkey (secretkey) .parseclaimsjws (token) .getBody (); 컬렉션 <? willedauthority> 당국 = arrays.stream (clartments.get (vercistities_key) .toString (). split ( ",")) // 사용자 권한 string.map (simplegrantedauthority :: new) .collect (collectors.tolist ()); // 요소를 granteDauthority 인터페이스 컬렉션으로 변환하십시오. 사용자 Principal = 새 사용자 (claims.getSubject (), "", 당국); 새로운 usernamepasswordauthenticationtoken을 반환합니다 (Principal, "", 당국); } // 토큰이 올바른지 여부를 확인하십시오. public boolean validateToken (String token) {try {jwts.parser (). setSigningKey (SecretKey) .parseclaimsjws (토큰); // 키 리턴으로 토큰을 확인하십시오. } catch (signatureException e) {// 서명 예외 log.info ( "Invalid JWT 서명"); log.trace ( "유효하지 않은 JWT 서명 트레이스 : {}", e); } catch (marformedjwtexception e) {// jwt 형식 오류 log.info ( "invalid jwt token."); log.trace ( "유효하지 않은 JWT 토큰 트레이스 : {}", e); } catch (expreiredjwtexception e) {// jwt 만료 된 log.info ( "만료 JWT 토큰"); log.trace ( "만료 된 JWT 토큰 트레이스 : {}", e); } catch (unsupportedjwtexception e) {// jwt log.info ( "지원되지 않은 JWT 토큰"); log.trace ( "지원되지 않은 JWT 토큰 트레이스 : {}", e); } catch (불법 행위 exception e) {// 매개 변수 오류 예외 log.info ( "JWT 토큰 핸들러가 유효하지 않습니다."); log.trace ( "JWT 토큰 핸들러 소형 처리기는 유효하지 않은 트레이스 : {}", e); } false를 반환합니다. }} 사용자 엔티티 클래스를 나타내는 userDetails 인터페이스 구현, 사용자 객체에 래핑되고 권한 및 기타 속성을 포함하며 Spring Security에서 사용할 수 있습니다.
공개 클래스 myuserDetails는 userDetails {private user user; public myuserDetails (사용자 사용자) {this.user = user; } @override public 컬렉션 <? getedauthority> getAuthorities () {list <brole> roboles = user.getRoles (); 목록 <getedauthority> 당국 = New ArrayList <> (); StringBuilder sb = new StringBuilder (); if (role.size ()> = 1) {for (역할 역할 : 역할) {userities.add (new SimplegrantEdauthority (role.getName ())); } 반환 당국; } 반환 당국; } return orviroyUtils.commaseParatedStringtoAuthorityList ( ""); } @override public String getPassword () {return user.getPassword (); } @override public String getUserName () {return user.getName (); } @override public boolean isaccountnonexpired () {return true; } @override public boolean isaccountnonlocked () {return true; } @override public boolean iscredentialsnonexpired () {return true; } @override public boolean isenabled () {return true; }} userDetails를 얻는 메소드가 하나만있는 userDetailsService 인터페이스를 구현하십시오. 데이터베이스에서 사용자 객체를 얻은 다음 userDetails로 싸서 반환 할 수 있습니다.
@ServicePublic Class MyUserDetailsService 구현 userDetailSService {@autowired userrepository userrepository; @override public userDetails loadUserByUserName (string s)은 usernamenotfoundexception {// 데이터베이스에서 사용자 객체로드 옵션 <user> userreepository.findbyName (s); // 디버깅의 경우 값이 존재하면 사용자 이름과 비밀번호가 출력됩니다. ifpresent ((value)-> system.out.println ( "username :"+value.getName ()+"사용자 암호 :"+value.getPassword ())); // 값이 더 이상 없으면 NULL 리턴 NULL 리턴 새 MyUserDetails (user.orelse (null)); }} 필터를 작성하십시오. 사용자가 토큰을 운반하는 경우 토큰을 얻고 토큰을 기반으로 인증 인증 객체를 생성하고 Spring Security의 권한 제어를 위해 SecurityContext에 저장합니다.
공개 클래스 jwtauthenticationTokenFilter genericfilterbean {private final logger log = loggerfactory.getLogger (jwtauthenticationTokenFilter.class); @autowired private jwttokenutils tokenprovider; @override public void dofilter (servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain)는 ioexception, servletexception {system.out.println ( "jwtauthenticationToknfilter"); {httpservletrequest httpreq = (httpservletrequest) servletrequest; 문자열 jwt = resolvetoken (httpreq); if (stringUtils.hastext (jwt) && this.tokenprovider.validateToken (jwt)) {// JWT가 올바른 인증 인증인지 확인 = this.tokenProvider.getAuthentication (jwt); // 사용자 인증 정보 정보 SecurityContexTholder.getContext (). setAuthentication (인증); // 사용자를 SecurityContext에 저장} FilterCain.dofilter (ServletRequest, ServletResponse); } catch (expriedjwtexception e) {// jwt invalid log.info ( "user {} - {}", e.getClaims (). getSubject (), e.getMessage ()); log.trace ( "보안 예외 추적 : {}", e); ((httpservletResponse) servletResponse) .setstatus (httpservletResponse.sc_unauthorized); }} private string resolvetoken (httpservletrequest request) {string bearertoken = request.getheader (WebsecurityConfig.authorization_header); // http 헤더에서 토큰을 얻습니다. if (stringUtils.hastext (bearertoken) && bearertoken.startswith ( "bearer")) {return bearertoken.substring (7, bearertoken.length ()); // 토큰 문자열을 반환하고 bearer를 제거합니다} string jwt = request.getParameter (websecurityConfig.authorization_token); // 요청 매개 변수에서 토큰을 가져옵니다. if (stringUtils.Hastext (jwt)) {return jwt; } return null; }} LoginController를 작성하십시오. 사용자 이름 및 비밀번호를 통해 사용자가 액세스 /인증 /로그인하고 LoginDTO 객체를 통해 수신하고 인증 객체를 만듭니다. 코드는 객체가 존재하는지 여부를 결정하기 위해 usernamepasswordauthenticationToken입니다. AuthenticationManager의 인증 방법을 통해 인증 객체를 확인하십시오. AuthenticationManager 구현 클래스 제공자 관리자는 AuthenticationProvider (Authentication Processing)를 통해 확인합니다. Default ProviderManager는 인증 처리를 위해 DaoAuthenticationProvider를 호출합니다. DaoAuthenticationProvider는 userDetailsService (인증 정보 소스)를 통해 사용자 데일을 얻습니다. 인증이 성공하면 권한이 포함 된 인증이 반환 된 다음 SecurityContexTholder.getAuthentication ()을 통해 SecurityContext로 설정하고, 인증에 따라 TOKEN을 생성하고, 사용자로 돌아갑니다.
@RestControllerPublic 클래스 LoginController {@autowired private userrepository userrepository; @autowired Private AuthenticationManager AuthenticationManager; @autowired private jwttokenutils jwttokenutils; @RequestMapping(value = "/auth/login",method = RequestMethod.POST) public String login(@Valid LoginDTO loginDTO, HttpServletResponse httpResponse) throws Exception{ //Create an Authentication authentication object through the username and password, and implement the class as UsernamePasswordAuthenticationToken UsernamePasswordAuthenticationToken authenticationToken = new usernamepasswordauthenticationToken (logindto.getusername (), logindto.getPassword ()); // 인증 객체가 비어 있지 않은 경우 if (Objects.Nonnull (authenticationToken)) {userRepository.FindByName (authenticationToken.getPrincipal (). toString ()) .ORELSETHROW (()-> 새로운 예외 ( "사용자가 존재하지 않는다"); } try {// authenticationManager (기본적으로 구현 된 기본값) authentication = authenticationManager.authenticate (authenticationTicate)를 통해 인증 객체를 확인합니다. // 인증을 SecurityContext SecurityContexTholder.getContext (). setAuthentication (Authentication)에 바인딩합니다. // 토큰 문자열 토큰 생성 = jwttokenutils.createToken (인증, false); // 토큰을 http 헤더 httpresponse.addheader (websecurityconfig.authorization_header, "bearer"+token)에 쓰십시오. "Bearer"+토큰을 반환합니다. } catch (badcredentialsexception 인증) {새 예외 ( "비밀번호 오류"); }}} 보안 구성 클래스를 작성하고 WebSecurityConfigurerAdapter를 상속 받고 구성 메소드를 대체하십시오.
@configuration@enablewebsecurity@enableglobalmethodsecurity (prepostenabled = true) public class websecurityconfig 확장 websecurityConfigurerAdapter {public static final string upportization_header = "인증"; public static final string alustization_token = "access_token"; @autowired private userDetailsService userDetailsService; @override protected void configure (AuthenticationManagerBuilder Auth) 예외 {auth // userDetailsService (userDetailsService) // 비밀번호 Encryption.PasswordEncoder 설정 (passwordEncoder ()); } @override protected void configure (httpsecurity http)는 예외를 던져 {// 요청 정책 http // close csrf 및 cors .cors (). . and () // http requests.authorizerequests () // 모든 사용자가 홈페이지에 액세스하고 로그인 할 수 있도록 허용합니다. . 및 () // set logout (). permitAll (); // http에서 jwt 필터를 추가합니다 .addfilterbefore (genericfilterbean (), usernamepasswordauthenticationfilter.class); } @bean public parbliceNcoder passwordEncoder () {return new bcryptpasswordencoder (); } @bean public genericfilterbean genericfilterbean genericfilterbean () {return new jwtauthenticationTokenFilter (); }} 테스트를 위해 컨트롤러를 작성하십시오
@RestControllerPublic Class USERCONTROLLER {@POSTMAPPING ( "/login") public String Login () {return "login"; } @getMapping ( "/") public String index () {return "Hello"; } @getMapping ( "/userPage") public String httpapi () {system.out.println (SecurityContexTholder.getContext (). getAuthentication (). getPrincipal ()); "userPage"를 반환합니다. } @getMapping ( "/adminPage") public String httpsuite () {return "userPage"; }}사례 소스 코드 다운로드 (로컬 다운로드)
요약
위는이 기사의 전체 내용입니다. 이 기사의 내용에 모든 사람의 연구 나 작업에 대한 특정 참조 가치가 있기를 바랍니다. 궁금한 점이 있으면 의사 소통을 위해 메시지를 남길 수 있습니다. Wulin.com을 지원 해주셔서 감사합니다.