国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - JAVA教程 - Spring Security結合JWT的方法教程

Spring Security結合JWT的方法教程

2021-03-05 14:41林塬 JAVA教程

這篇文章主要給大家介紹了關于Spring Security結合JWT的方法教程,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

概述

眾所周知使用 JWT 做權限驗證,相比 Session 的優點是,Session 需要占用大量服務器內存,并且在多服務器時就會涉及到共享 Session 問題,在手機等移動端訪問時比較麻煩

而 JWT 無需存儲在服務器,不占用服務器資源(也就是無狀態的),用戶在登錄后拿到 Token 后,訪問需要權限的請求時附上 Token(一般設置在Http請求頭),JWT 不存在多服務器共享的問題,也沒有手機移動端訪問問題,若為了提高安全,可將 Token 與用戶的 IP 地址綁定起來

前端流程

用戶通過 AJAX 進行登錄得到一個 Token

之后訪問需要權限請求時附上 Token 進行訪問

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!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"></script>
 <script type="application/javascript">
  var header = "";
  function login() {
   $.post("http://localhost:8080/auth/login", {
    username: $("#username").val(),
    password: $("#password").val()
   }, function (data) {
    console.log(data);
    header = data;
   })
  }
  function toUserPageBtn() {
   $.ajax({
    type: "get",
    url: "http://localhost:8080/userpage",
    beforeSend: function (request) {
     request.setRequestHeader("Authorization", header);
    },
    success: function (data) {
     console.log(data);
    }
   });
  }
 </script>
</head>
<body>
 <fieldset>
  <legend>Please Login</legend>
  <label>UserName</label><input type="text" id="username">
  <label>Password</label><input type="text" id="password">
  <input type="button" onclick="login()" value="Login">
 </fieldset>
 <button id="toUserPageBtn" onclick="toUserPageBtn()">訪問UserPage</button>
</body>
</html>

后端流程(Spring Boot + Spring Security + JJWT)

思路:

  • 創建用戶、權限實體類與數據傳輸對象
  • 編寫 Dao 層接口,用于獲取用戶信息
  • 實現 UserDetails(Security 支持的用戶實體對象,包含權限信息)
  • 實現 UserDetailsSevice(從數據庫中獲取用戶信息,并包裝成UserDetails)
  • 編寫 JWTToken 生成工具,用于生成、驗證、解析 Token
  • 配置 Security,配置請求處理 與 設置 UserDetails 獲取方式為自定義的 UserDetailsSevice
  • 編寫 LoginController,接收用戶登錄名密碼并進行驗證,若驗證成功返回 Token 給用戶
  • 編寫過濾器,若用戶請求頭或參數中包含 Token 則解析,并生成 Authentication,綁定到 SecurityContext ,供 Security 使用
  • 用戶訪問了需要權限的頁面,卻沒附上正確的 Token,在過濾器處理時則沒有生成 Authentication,也就不存在訪問權限,則無法訪問,否之訪問成功

編寫用戶實體類,并插入一條數據

User(用戶)實體類

?
1
2
3
4
5
6
7
8
9
10
11
12
@Data
@Entity
public class User {
 @Id
 @GeneratedValue
 private int id;
 private String name;
 private String password;
 @ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER)
 @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "uid", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "rid", referencedColumnName = "id")})
 private List<Role> roles;
}

Role(權限)實體類

?
1
2
3
4
5
6
7
8
9
10
@Data
@Entity
public class Role {
 @Id
 @GeneratedValue
 private int id;
 private String name;
 @ManyToMany(mappedBy = "roles")
 private List<User> users;
}

插入數據

User 表

 

id name password
1 linyuan 123

 

Role 表

 

id name
1 USER

 

User_ROLE 表

 

uid rid
1 1

 

Dao 層接口,通過用戶名獲取數據,返回值為 Java8 的 Optional 對象

?
1
2
3
public interface UserRepository extends Repository<User,Integer> {
 Optional<User> findByName(String name);
}

編寫 LoginDTO,用于與前端之間數據傳輸

?
1
2
3
4
5
6
7
@Data
public class LoginDTO implements Serializable {
 @NotBlank(message = "用戶名不能為空")
 private String username;
 @NotBlank(message = "密碼不能為空")
 private String password;
}

編寫 Token 生成工具,利用 JJWT 庫創建,一共三個方法:生成 Token(返回String)、解析 Token(返回Authentication認證對象)、驗證 Token(返回布爾值)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
@Component
public class JWTTokenUtils {
 private final Logger log = LoggerFactory.getLogger(JWTTokenUtils.class);
 private static final String AUTHORITIES_KEY = "auth";
 private String secretKey;   //簽名密鑰
 private long tokenValidityInMilliseconds;  //失效日期
 private long tokenValidityInMillisecondsForRememberMe;  //(記住我)失效日期
 @PostConstruct
 public void init() {
  this.secretKey = "Linyuanmima";
  int secondIn1day = 1000 * 60 * 60 * 24;
  this.tokenValidityInMilliseconds = secondIn1day * 2L;  this.tokenValidityInMillisecondsForRememberMe = secondIn1day * 7L;
 }
 private final static long EXPIRATIONTIME = 432_000_000;
 //創建Token
 public String createToken(Authentication authentication, Boolean rememberMe){
  String authorities = authentication.getAuthorities().stream()  //獲取用戶的權限字符串,如 USER,ADMIN
    .map(GrantedAuthority::getAuthority)
    .collect(Collectors.joining(","));
  long now = (new Date()).getTime();    //獲取當前時間戳
  Date validity;           //存放過期時間
  if (rememberMe){
   validity = new Date(now + this.tokenValidityInMilliseconds);
  }else {
   validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
  }
  return Jwts.builder()         //創建Token令牌
    .setSubject(authentication.getName())   //設置面向用戶
    .claim(AUTHORITIES_KEY,authorities)    //添加權限屬性
    .setExpiration(validity)      //設置失效時間
    .signWith(SignatureAlgorithm.HS512,secretKey) //生成簽名
    .compact();
 }
 //獲取用戶權限
 public Authentication getAuthentication(String token){
  System.out.println("token:"+token);
  Claims claims = Jwts.parser()       //解析Token的payload
    .setSigningKey(secretKey)
    .parseClaimsJws(token)
    .getBody();
  Collection<? extends GrantedAuthority> authorities =
    Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))   //獲取用戶權限字符串
    .map(SimpleGrantedAuthority::new)
    .collect(Collectors.toList());             //將元素轉換為GrantedAuthority接口集合
  User principal = new User(claims.getSubject(), "", authorities);
  return new UsernamePasswordAuthenticationToken(principal, "", authorities);
 }
 //驗證Token是否正確
 public boolean validateToken(String token){
  try {
   Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); //通過密鑰驗證Token
   return true;
  }catch (SignatureException e) {          //簽名異常
   log.info("Invalid JWT signature.");
   log.trace("Invalid JWT signature trace: {}", e);
  } catch (MalformedJwtException e) {         //JWT格式錯誤
   log.info("Invalid JWT token.");
   log.trace("Invalid JWT token trace: {}", e);
  } catch (ExpiredJwtException e) {         //JWT過期
   log.info("Expired JWT token.");
   log.trace("Expired JWT token trace: {}", e);
  } catch (UnsupportedJwtException e) {        //不支持該JWT
   log.info("Unsupported JWT token.");
   log.trace("Unsupported JWT token trace: {}", e);
  } catch (IllegalArgumentException e) {        //參數錯誤異常
   log.info("JWT token compact of handler are invalid.");
   log.trace("JWT token compact of handler are invalid trace: {}", e);
  }
  return false;
 }
}

實現 UserDetails 接口,代表用戶實體類,在我們的 User 對象上在進行包裝,包含了權限等性質,可以供 Spring Security 使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class MyUserDetails implements UserDetails{
 private User user;
 public MyUserDetails(User user) {
  this.user = user;
 }
 @Override
 public Collection<? extends GrantedAuthority> getAuthorities() {
  List<Role> roles = user.getRoles();
  List<GrantedAuthority> authorities = new ArrayList<>();
  StringBuilder sb = new StringBuilder();
  if (roles.size()>=1){
   for (Role role : roles){
    authorities.add(new SimpleGrantedAuthority(role.getName()));
   }
   return authorities;
  }
  return AuthorityUtils.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;
 }
}

實現 UserDetailsService 接口,該接口僅有一個方法,用來獲取 UserDetails,我們可以從數據庫中獲取 User 對象,然后將其包裝成 UserDetails 并返回

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MyUserDetailsService implements UserDetailsService {
 @Autowired
 UserRepository userRepository;
 @Override
 public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  //從數據庫中加載用戶對象
  Optional<User> user = userRepository.findByName(s);
  //調試用,如果值存在則輸出下用戶名與密碼
  user.ifPresent((value)->System.out.println("用戶名:"+value.getName()+" 用戶密碼:"+value.getPassword()));
  //若值不再則返回null
  return new MyUserDetails(user.orElse(null));
 }
}

編寫過濾器,用戶如果攜帶 Token 則獲取 Token,并根據 Token 生成 Authentication 認證對象,并存放到 SecurityContext 中,供 Spring Security 進行權限控制

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class JwtAuthenticationTokenFilter extends GenericFilterBean {
 private final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
 @Autowired
 private JWTTokenUtils tokenProvider;
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  System.out.println("JwtAuthenticationTokenFilter");
  try {
   HttpServletRequest httpReq = (HttpServletRequest) servletRequest;
   String jwt = resolveToken(httpReq);
   if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {   //驗證JWT是否正確
    Authentication authentication = this.tokenProvider.getAuthentication(jwt);  //獲取用戶認證信息
    SecurityContextHolder.getContext().setAuthentication(authentication);   //將用戶保存到SecurityContext
   }
   filterChain.doFilter(servletRequest, servletResponse);
  }catch (ExpiredJwtException e){          //JWT失效
   log.info("Security exception for user {} - {}",
     e.getClaims().getSubject(), e.getMessage());
   log.trace("Security exception trace: {}", e);
   ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  }
 }
 private String resolveToken(HttpServletRequest request){
  String bearerToken = request.getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);   //從HTTP頭部獲取TOKEN
  if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
   return bearerToken.substring(7, bearerToken.length());        //返回Token字符串,去除Bearer
  }
  String jwt = request.getParameter(WebSecurityConfig.AUTHORIZATION_TOKEN);    //從請求參數中獲取TOKEN
  if (StringUtils.hasText(jwt)) {
   return jwt;
  }
  return null;
 }
}

編寫 LoginController,用戶通過用戶名、密碼訪問 /auth/login,通過 LoginDTO 對象接收,創建一個 Authentication 對象,代碼中為 UsernamePasswordAuthenticationToken,判斷對象是否存在,通過 AuthenticationManager 的 authenticate 方法對認證對象進行驗證,AuthenticationManager 的實現類 ProviderManager 會通過 AuthentionProvider(認證處理) 進行驗證,默認 ProviderManager 調用 DaoAuthenticationProvider 進行認證處理,DaoAuthenticationProvider 中會通過 UserDetailsService(認證信息來源) 獲取 UserDetails ,若認證成功則返回一個包含權限的 Authention,然后通過 SecurityContextHolder.getContext().setAuthentication() 設置到 SecurityContext 中,根據 Authentication 生成 Token,并返回給用戶

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@RestController
public class 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{
  //通過用戶名和密碼創建一個 Authentication 認證對象,實現類為 UsernamePasswordAuthenticationToken
  UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(),loginDTO.getPassword());
  //如果認證對象不為空
  if (Objects.nonNull(authenticationToken)){
   userRepository.findByName(authenticationToken.getPrincipal().toString())
     .orElseThrow(()->new Exception("用戶不存在"));
  }
  try {
   //通過 AuthenticationManager(默認實現為ProviderManager)的authenticate方法驗證 Authentication 對象
   Authentication authentication = authenticationManager.authenticate(authenticationToken);
   //將 Authentication 綁定到 SecurityContext
   SecurityContextHolder.getContext().setAuthentication(authentication);
   //生成Token
   String token = jwtTokenUtils.createToken(authentication,false);
   //將Token寫入到Http頭部
   httpResponse.addHeader(WebSecurityConfig.AUTHORIZATION_HEADER,"Bearer "+token);
   return "Bearer "+token;
  }catch (BadCredentialsException authentication){
   throw new Exception("密碼錯誤");
  }
 }
}

編寫 Security 配置類,繼承 WebSecurityConfigurerAdapter,重寫 configure 方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 public static final String AUTHORIZATION_HEADER = "Authorization";
 public static final String AUTHORIZATION_TOKEN = "access_token";
 @Autowired
 private UserDetailsService userDetailsService;
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth
    //自定義獲取用戶信息
    .userDetailsService(userDetailsService)
    //設置密碼加密
    .passwordEncoder(passwordEncoder());
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  //配置請求訪問策略
  http
    //關閉CSRF、CORS
    .cors().disable()
    .csrf().disable()
    //由于使用Token,所以不需要Session
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
    //驗證Http請求
    .authorizeRequests()
    //允許所有用戶訪問首頁 與 登錄
    .antMatchers("/","/auth/login").permitAll()
    //其它任何請求都要經過認證通過
    .anyRequest().authenticated()
    //用戶頁面需要用戶權限
    .antMatchers("/userpage").hasAnyRole("USER")
    .and()
    //設置登出
    .logout().permitAll();
  //添加JWT filter 在
  http
    .addFilterBefore(genericFilterBean(), UsernamePasswordAuthenticationFilter.class);
 }
 @Bean
 public PasswordEncoder passwordEncoder() {
  return new BCryptPasswordEncoder();
 }
 @Bean
 public GenericFilterBean genericFilterBean() {
  return new JwtAuthenticationTokenFilter();
 }
}

編寫用于測試的Controller

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RestController
public 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());
  return "userpage";
 }
 @GetMapping("/adminpage")
 public String httpSuite() {
  return "userpage";
 }
}

案例源碼下載

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:http://www.jianshu.com/p/fceb45733355

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 色狠狠一区二区三区香蕉 | 亚洲国产aⅴ成人精品无吗 成人午夜视频在线观看 | 韩国理论电影在线 | 一区二区在线视频 | 久久久精 | 97色婷婷成人综合在线观看 | 免费激情 | 夜夜操操操操 | 亚洲视频在线看 | 日韩毛片| 午夜电影网址 | 成人午夜精品 | 五月婷伊人 | av网站观看 | 精品国产乱码久久久久久丨区2区 | 久久精品中文字幕大胸 | 亚洲视频中文字幕在线观看 | 亚洲国产成人av好男人在线观看 | 91精品中文字幕一区二区三区 | 狠狠综合久久 | 国产色 | 亚洲视屏 | 亚洲午夜在线 | 久在线| 久久久久久国产一级毛片高清版 | 不卡av一区二区三区 | 一二三四区视频在线观看 | 亚洲欧美在线观看 | 黄色一级片在线观看 | 日韩国产精品一区二区三区 | 国产精品一二区 | theporn国产在线精品 | 亚洲视频欧美视频 | 午夜欧美| 亚洲成人一级片 | 99久久免费精品国产男女性高好 | 亚洲精品福利在线观看 | 一区二区三区欧美在线 | 亚洲色图综合 | 欧美国产一区二区三区 | 二区三区在线 |