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下的链接
到此这篇关于SpringBoot集成Spring security JWT实现接口权限认证的文章就介绍到这了,更多相关SpringBoot 接口权限认证内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!
- 本文固定链接: https://zxbcw.cn/post/209581/
- 转载请注明:必须在正文中标注并保留原文链接
- QQ群: PHP高手阵营官方总群(344148542)
- QQ群: Yii2.0开发(304864863)