SpringBoot整合Spring security JWT實現介面許可權認證
1、新增依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、整合JWT工具類(JwtUtils)
package com.dreamteam.chdapp.utils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* @Author HeYunHui
* @create 2020/11/15 14:12
*/
public class JwtUtils {
private static final Logger logger= LoggerFactory.getLogger(JwtUtils.class);
public static final long EXPIRATION_TIME=60*60*1000;// 令牌環有效期
public static final String SECRET="abc123456def";//令牌環金鑰
public static final String TOKEN_PREFIX="Bearer";//令牌環頭標識
public static final String HEADER_STRING="Passport";//配置令牌環在http heads中的鍵值
public static final String ROLE="ROLE";//自定義欄位-角色欄位
//生成令牌環
public static String generateToken(String userRole,String userid){
HashMap<String,Object> map=new HashMap<>();
map.put(ROLE,userRole);
map.put("userid",userid);
String jwt= Jwts.builder()
.setClaims(map)
.setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512,SECRET)
.compact();
return TOKEN_PREFIX+" "+jwt;
}
//生成令牌環
public static String generateToken(String userRole,String userid,long exprationtime){
HashMap<String,Object> map=new HashMap<>();
map.put(ROLE,userRole);
map.put("userid",userid);
String jwt= Jwts.builder()
.setClaims(map)
.setExpiration(new Date(System.currentTimeMillis()+exprationtime))
.signWith(SignatureAlgorithm.HS512,SECRET)
.compact();
return TOKEN_PREFIX+" "+jwt;
}
//令牌環校驗
public static Map<String,Object> validateTokenAndGetClaims(HttpServletRequest request){
String token=request.getHeader(HEADER_STRING);
if(token==null){
throw new TokenValidationException("Missing Token");
}
else{
Map<String,Object> body= Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX,""))
.getBody();
return body;
}
}
static class TokenValidationException extends RuntimeException{
public TokenValidationException(String msg){
super(msg);
}
}
}
3、整合JWT filter(攔截器/過濾器)
package com.dreamteam.chdapp.filter;
import com.dreamteam.chdapp.utils.JwtUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import static com.dreamteam.chdapp.utils.JwtUtils.ROLE;
/**
* @Author HeYunHui
* @create 2020/11/15 14:46
*/
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final PathMatcher pathmatcher = new AntPathMatcher();
private String[] protectUrlPattern = {"/manage/**", "/member/**", "/auth/**"}; //哪 些請求需要進行安全校驗
public JwtAuthenticationFilter() {
}
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
//是不是可以在這裡做多種方式登入呢
try {
if (isProtectedUrl(httpServletRequest)) {
Map<String, Object> claims = JwtUtils.validateTokenAndGetClaims(httpServletRequest);
String role = String.valueOf(claims.get(ROLE));
String userid = String.valueOf(claims.get("userid"));
//最關鍵的部分就是這裡, 我們直接注入了
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
userid, null, Arrays.asList(() -> role)
));
}
} catch (Exception e) {
e.printStackTrace();
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
return;
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
//是否是保護連線
private boolean isProtectedUrl(HttpServletRequest request) {
boolean flag = false;
for (int i = 0; i < protectUrlPattern.length; i++) {
if (pathmatcher.match(protectUrlPattern[i], request.getServletPath())) {
return true;
}
}
return false;
}
}
4、配置JWT config類(配置類)
跨域訪問:客戶端與服務端域名不同或是埠號不同。防止跨域攻擊
package edu.ynmd.cms.config;
import edu.ynmd.cms.filter.JwtAuthenticationFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.StrictHttpFirewall;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowUrlEncodedSlash(true);
return firewall;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors() //允許跨域訪問
.and()
.authorizeRequests()
.antMatchers("/").authenticated() //配置那些url需要進行校驗--所有請求都需要校驗"/"
.antMatchers("/public/**").permitAll() //那些請求不需要校驗
.anyRequest().authenticated() //自定義校驗類
.and()
.addFilterBefore(new JwtAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)//關閉session
;
}
}
5、Action註解
在Controller類中新增
@CrossOrigin
@RestController
@PreAuthorize("hasAuthority('admin')") //配置角色,擁有該角色的使用者方可訪問
@RequestMapping("/manage")
postman測試http://localhost:7070/manage/userList,不可訪問
public開頭的可以訪問
6、token令牌環,訪問需校驗的資源
public的Controller類新增
@PostMapping("/login")
@ResponseBody
public HashMap<String,String> login(
@RequestBody Account account) throws IOException {
// Users u=manageService.getUserByUserNameAndPass(account.username,account.password);
if(account.username.equals("admin")&&account.password.equals("123456")){
// if(u!=null){
String jwt= JwtUtils.generateToken("admin","123456789abc");
// String jwt= JwtUtils.generateToken(u.getRoleid(),u.getUsersid());
return new HashMap<String,String>(){{
put("msg","ok");
put("token",jwt);
// put("role",u.getRoleid());
put("role","admin");
}};
}
else {
//return new ResponseEntity(HttpStatus.UNAUTHORIZED);
return new HashMap<String,String>(){{
put("msg","error");
put("token","error");
}};
}
}
public static class Account{
public String username;
public String password;
}
postman測試,隨便輸使用者名稱密碼
輸入程式碼中的使用者名稱密碼
去JWT官網https://jwt.io/,頁面下滑,將得到的token輸入,得到
manage的Controller類中新增測試
@GetMapping("testSecurityResource")
@ResponseBody
public String testSecurityResource() throws Exception{
return "受保護的資源";
}
用postman訪問http://localhost:7070/manage/testSecurityResource,返回結果
7、service工具類
通用請求處理
package com.dreamteam.chdapp.controller.common;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
/**
* 通用請求處理
* @Author HeYunHui
* @create 2020/11/14 15:38
*/
@Controller
public class CommonController {
protected static final Logger log= LoggerFactory.getLogger(CommonController.class);
/**
* 字串為空
* @param value
* @return
*/
public static boolean isNullOrSpace(String value){
if(value==null){
return true;
}
else {
if(value.equals("")){
return true;
}
else {
return false;
}
}
}
}
Service層
String getCurrentUserId();//從令牌環中獲取userid
String getCurrentRole();//從令牌環中獲取角色id
ServiceImpl
/**
* 獲取當前登入用的的Id
* @return
*/
@Override
public String getCurrentUserId() {
String userid= (String) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
if(CommonController.isNullOrSpace(userid)){
return null;
}
else {
return userid;
}
}
/**
* 獲取當前登入使用者的角色
* @return
*/
@Override
public String getCurrentRole() {
String role=null;
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
if(CommonController.isNullOrSpace(role)){
return null;
}
else{
return role;
}
}
修改manage的Controller類
@GetMapping("testSecurityResource")
@ResponseBody
public String testSecurityResource() throws Exception{
String userid=userInfoService.getCurrentUserId();
String role=userInfoService.getCurrentRole();
return "受保護的資源,當前使用者的id是"+userid+"當前使用者的角色是"+role;
}
用postman測試
這是前面自定義的
8、識別token資訊
如果將下圖中的角色換掉,將不能訪問
9、自動更新令牌環
新增Controller類
package com.dreamteam.chdapp.controller;
import com.dreamteam.chdapp.controller.common.CommonController;
import com.dreamteam.chdapp.utils.JwtUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.HashMap;
/**
* 令牌環自動更新
* @Author HeYunHui
* @create 2020/11/16 17:24
* @PreAuthorize("hasAuthority('admin')")//只允許有admin角色的使用者訪問 hasAnyAuthority([auth1,auth2])
*/
@CrossOrigin
@RestController
@PreAuthorize("hasAnyAuthority('admin','member')")
@RequestMapping("/auth")
public class AuthController {
/**
* 更新令牌環資訊
* @param request
* @return
*/
@GetMapping("refreshToken")
@ResponseBody
public HashMap<String,String> refreshToken(HttpServletRequest request){
String role=null;
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
// UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
String userid= (String)SecurityContextHolder.getContext().getAuthentication() .getPrincipal();
if(CommonController.isNullOrSpace(role)){
return new HashMap<String,String>(){{
put("token","error");
}};
}
else{
String jwt="";
//一小時
jwt= JwtUtils.generateToken(role,userid,60*60*1000);
HashMap<String,String> m=new HashMap<>();
m.put("token",jwt);
return m;
}
}
/**
* 獲取當前登入使用者的角色
* @return
*/
@GetMapping("getRole")
@ResponseBody
public HashMap<String,String> getRoleByToken(){
String role="";
String userid="";
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for (GrantedAuthority authority : authorities) {
role = authority.getAuthority();
}
if(CommonController.isNullOrSpace(role)){
return new HashMap<String,String>(){{
put("role","error");
}};
}
else{
HashMap<String,String> m=new HashMap<>();
m.put("role",role);
return m;
}
}
}
用postman測試
10、使用資料庫儲存使用者資訊
(1)實體類
package com.dreamteam.chdapp.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
/**
* 表名
*/
@TableName("users")
public class Users {
@TableId(type = IdType.AUTO)
private String usrId;
private String usrName;
private String usrTel;
private String usrPwd;
private String usrType;
}
UserMapper
package com.dreamteam.chdapp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dreamteam.chdapp.entity.Users;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Author HeYunHui
* @create 2020/11/11 21:50
*/
@Repository
@Mapper
public interface UserMapper extends BaseMapper<Users> {
List<Users> getUsersByUsrNameAndPwd(@Param("usrName")String usrName, @Param("usrPwd") String usrPwd);
}
UsersMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dreamteam.chdapp.mapper.UserMapper">
<select id="getUsersByUsrNameAndPwd" resultType="com.dreamteam.chdapp.entity.Users">
select * from users where #{usrName}=usr_name and #{usrPwd}=usr_pwd
</select>
</mapper>
service
Users getUsersByUsrNameAndPwd(String usrName,String usrPwd);
serviceImpl JWT獲取使用者名稱密碼
@Override
public Users getUsersByUsrNameAndPwd(String usrName, String usrPwd) {
List<Users> ul=userMapper.getUsersByUsrNameAndPwd(usrName,usrPwd);
if(ul.size()>0){
return ul.get(0);
}
return null;
}
Controller
@PostMapping("/login")
@ResponseBody
public HashMap<String,String> login(
@RequestBody Account account) throws IOException {
Users u=userInfoService.getUsersByUsrNameAndPwd(account.username,account.password);
// if(account.username.equals("admin")&&account.password.equals("123456")){
if(u!=null){
// String jwt= JwtUtils.generateToken("admin","123456789abc");
String jwt= JwtUtils.generateToken(u.getUsrType(),u.getUsrId());
return new HashMap<String,String>(){{
put("msg","ok");
put("token",jwt);
put("role",u.getUsrType());
// put("role","admin");
}};
}
else {
//return new ResponseEntity(HttpStatus.UNAUTHORIZED);
return new HashMap<String,String>(){{
put("msg","error");
put("token","error");
}};
}
}
public static class Account{
public String username;
public String password;
}
postman測試
a.登入,生成token
b.輸入token訪問manage下的連結
相關文章
- spring security許可權認證Spring
- RBAC許可權---SpringBoot整合SecuritySpring Boot
- Spring Security 實戰乾貨:使用 JWT 認證訪問介面SpringJWT
- springBoot整合spring security實現許可權管理(單體應用版)--築基初期Spring Boot
- springBoot整合spring security+JWT實現單點登入與許可權管理前後端分離--築基中期Spring BootJWT後端
- Spring Security + jwt 許可權系統設計,包含SQLSpringJWTSQL
- Hyperf 完整專案-1-jwt 許可權認證JWT
- Spring boot 入門(四):整合 Shiro 實現登陸認證和許可權管理Spring Boot
- springboot + shiro 實現登入認證和許可權控制Spring Boot
- Spring Security OAuth2.0認證授權三:使用JWT令牌SpringOAuthJWT
- 基於Spring Security實現許可權管理系統Spring
- springcloud-gateway整合jwt+jcasbin實現許可權控制SpringGCCloudGatewayJWT
- 基於Spring Security和 JWT的許可權系統設計SpringJWT
- Spring Security認證器實現Spring
- Spring Boot Security 整合 JWT 實現 無狀態的分散式API介面Spring BootJWT分散式API
- Spring Security實現統一登入與許可權控制Spring
- springboot(十四):springboot整合shiro-登入認證和許可權管理Spring Boot
- spring-boot-plus整合Shiro+JWT許可權管理SpringbootJWT
- Spring Security(一):整合JWTSpringJWT
- Hadoop 許可權認證Hadoop
- 七、Spring Boot整合Spring Security之前後分離認證最佳實現Spring Boot
- uniapp 小程式 Laravel+jwt 許可權認證完整系列APPLaravelJWT
- 完整的 uni-App+Laravel+jwt-auth 小程式許可權認證APPLaravelJWT
- 使用JWT實現Spring Boot令牌認證JWTSpring Boot
- 從零開始學Spring Boot系列-整合Spring Security實現使用者認證與授權Spring Boot
- Spring Boot (十四): Spring Boot 整合 Shiro-登入認證和許可權管理Spring Boot
- NODE + JWT + Mongo(簡單實現許可權管理)JWTGo
- 【Spring Security】實現多種認證方式Spring
- spring boot 利用註解實現許可權驗證Spring Boot
- SpringBoot整合SpringSecurityOauth2實現鑑權-動態許可權Spring BootGseOAuth
- [譯] JWT 整合 Spring Security OAuthJWTSpringOAuth
- Spring security(四)-spring boot +spring security簡訊認證+redis整合Spring BootRedis
- 最新版 Spring Security,該如何實現動態許可權管理?Spring
- Spring Cloud實戰 | 最終篇:Spring Cloud Gateway+Spring Security OAuth2整合統一認證授權平臺下實現登出使JWT失效方案SpringCloudGatewayOAuthJWT
- 基於Spring Security Oauth2的SSO單點登入+JWT許可權控制實踐SpringOAuthJWT
- 深入淺出:使用Java和Spring Security實現認證與授權JavaSpring
- SpringBoot整合Shiro+MD5+Salt+Redis實現認證和動態許可權管理(上)----築基中期Spring BootRedis
- 【認證與授權】Spring Security系列之認證流程解析Spring