- 基于JWT的API鉴权
- 基于拦截器的token与鉴权
如果我们每个方法都去写一段代码,冗余度太高,不利于维护,那如何做使我们的代码看起来更清爽呢?我们可以 将这段代码放入拦截器去实现
Spring中的拦截器
Spring为我们提供了org.springframework.web.servlet.handler.HandlerInterceptorAdapter这个适配器,继承此 类,可以非常方便的实现自己的拦截器。他有三个方法:分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面)
1.在preHandle中,可以进行编码、安全控制等处理; 2.在postHandle中,有机会修改ModelAndView; 3.
在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。
签发用户API权限
在系统微服务的 修改签发token的登录服务添加API权限
共享控制类,获取公共属性,@ModelAttribute注解是在执行其他控制类方法前执行.
package com.ihrm.common.controller;
import io.jsonwebtoken.Claims;
import org.springframework.web.bind.annotation.ModelAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BaseController {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected String companyId;
protected String companyName;
protected Claims claims;
@ModelAttribute
public void setResAnReq(HttpServletRequest request,HttpServletResponse response) {
this.request = request;
this.response = response;
Object obj = request.getAttribute("user_claims");
if(obj != null) {
this.claims = (Claims) obj;
this.companyId = (String)claims.get("companyId");
this.companyName = (String)claims.get("companyName");
}
}
}
用户登入控制类:
package com.ihrm.system.controller;
import com.ihrm.common.controller.BaseController;
import com.ihrm.common.entity.PageResult;
import com.ihrm.common.entity.Result;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.utils.JwtUtils;
import com.ihrm.common.utils.PermissionConstants;
import com.ihrm.domain.system.Permission;
import com.ihrm.domain.system.Role;
import com.ihrm.domain.system.response.ProfileResult;
import com.ihrm.domain.system.User;
import com.ihrm.domain.system.response.UserResult;
import com.ihrm.system.service.PermissionService;
import com.ihrm.system.service.RoleService;
import com.ihrm.system.service.UserService;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//1.解决跨域
@CrossOrigin
//2.声明restContoller
@RestController
//3.设置父路径
@RequestMapping(value="/sys")
public class UserController extends BaseController {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Autowired
private JwtUtils jwtUtils;
/**
* 分配角色
*/
@RequestMapping(value = "/user/assignRoles", method = RequestMethod.PUT)
public Result assignRoles(@RequestBody Map<String,Object> map) {
//1.获取被分配的用户id
String userId = (String) map.get("id");
//2.获取到角色的id列表
List<String> roleIds = (List<String>) map.get("roleIds");
//3.调用service完成角色分配
userService.assignRoles(userId,roleIds);
return new Result(ResultCode.SUCCESS);
}
/**
* 保存
*/
@RequestMapping(value = "/user", method = RequestMethod.POST)
public Result save(@RequestBody User user) {
//1.设置保存的企业id
user.setCompanyId(companyId);
user.setCompanyName(companyName);
//2.调用service完成保存企业
userService.save(user);
//3.构造返回结果
return new Result(ResultCode.SUCCESS);
}
/**
* 查询企业的部门列表
* 指定企业id
*/
@RequestMapping(value = "/user", method = RequestMethod.GET)
public Result findAll(int page, int size, @RequestParam Map map) {
//1.获取当前的企业id
map.put("companyId",companyId);
//2.完成查询
Page<User> pageUser = userService.findAll(map,page,size);
//3.构造返回结果
PageResult pageResult = new PageResult(pageUser.getTotalElements(),pageUser.getContent());
return new Result(ResultCode.SUCCESS, pageResult);
}
/**
* 根据ID查询user
*/
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public Result findById(@PathVariable(value = "id") String id) {
// 添加 roleIds (用户已经具有的角色id数组)
User user = userService.findById(id);
UserResult userResult = new UserResult(user);
return new Result(ResultCode.SUCCESS, userResult);
}
/**
* 修改User
*/
@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)
public Result update(@PathVariable(value = "id") String id, @RequestBody User user) {
//1.设置修改的部门id
user.setId(id);
//2.调用service更新
userService.update(user);
return new Result(ResultCode.SUCCESS);
}
/**
* 根据id删除
*/
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE,name = "API-USER-DELETE")
public Result delete(@PathVariable(value = "id") String id) {
userService.deleteById(id);
return new Result(ResultCode.SUCCESS);
}
/**
* 用户登录
* 1.通过service根据mobile查询用户
* 2.比较password
* 3.生成jwt信息
*
*/
@RequestMapping(value="/login",method = RequestMethod.POST)
public Result login(@RequestBody Map<String,String> loginMap) {
String mobile = loginMap.get("mobile");
String password = loginMap.get("password");
User user = userService.findByMobile(mobile);
//登录失败
if(user == null || !user.getPassword().equals(password)) {
return new Result(ResultCode.MOBILEORPASSWORDERROR);
}else {
//登录成功
//api权限字符串
StringBuilder sb = new StringBuilder();
//获取到所有的可访问API权限
for (Role role : user.getRoles()) {
for (Permission perm : role.getPermissions()) {
if(perm.getType() == PermissionConstants.PERMISSION_API) {
sb.append(perm.getCode()).append(",");
}
}
}
Map<String,Object> map = new HashMap<>();
map.put("apis",sb.toString());//可访问的api权限字符串
map.put("companyId",user.getCompanyId());
map.put("companyName",user.getCompanyName());
String token = jwtUtils.createJwt(user.getId(), user.getUsername(), map);
return new Result(ResultCode.SUCCESS,token);
}
}
/**
* 用户登录成功之后,获取用户信息
* 1.获取用户id
* 2.根据用户id查询用户
* 3.构建返回值对象
* 4.响应
*/
@RequestMapping(value="/profile",method = RequestMethod.POST)
public Result profile(HttpServletRequest request) throws Exception {
String userid = claims.getId();
//获取用户信息
User user = userService.findById(userid);
//根据不同的用户级别获取用户权限
ProfileResult result = null;
if("user".equals(user.getLevel())) {
result = new ProfileResult(user);
}else {
Map map = new HashMap();
if("coAdmin".equals(user.getLevel())) {
map.put("enVisible","1");
}
List<Permission> list = permissionService.findAll(map);
result = new ProfileResult(user,list);
}
return new Result(ResultCode.SUCCESS,result);
}
}
(1)在ihrm-common下添加拦截器 JwtInterceptor
package com.ihrm.common.interceptor;
import com.ihrm.common.entity.ResultCode;
import com.ihrm.common.exception.CommonException;
import com.ihrm.common.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 自定义拦截器
* 继承HandlerInterceptorAdapter
*
* preHandle:进入到控制器方法之前执行的内容
* boolean:
* true:可以继续执行控制器方法
* false:拦截
* posthandler:执行控制器方法之后执行的内容
* afterCompletion:响应结束之前执行的内容
*
* 1.简化获取token数据的代码编写
* 统一的用户权限校验(是否登录)
* 2.判断用户是否具有当前访问接口的权限
*
*/
@Component
public class JwtInterceptor extends HandlerInterceptorAdapter {
/**
* 简化获取token数据的代码编写(判断是否登录)
* 1.通过request获取请求token信息
* 2.从token中解析获取claims
* 3.将claims绑定到request域中
*/
@Autowired
private JwtUtils jwtUtils;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 1.通过request获取请求token信息
String authorization = request.getHeader("Authorization");
//判断请求头信息是否为空,或者是否已Bearer开头
if(!StringUtils.isEmpty(authorization) && authorization.startsWith("Bearer")) {
//获取token数据
String token = authorization.replace("Bearer ","");
//解析token获取claims
Claims claims = jwtUtils.parseJwt(token);
if(claims != null) {
//通过claims获取到当前用户的可访问API权限字符串
String apis = (String) claims.get("apis"); //api-user-delete,api-user-update
//通过handler
HandlerMethod h = (HandlerMethod) handler;
//获取接口上的reqeustmapping注解
RequestMapping annotation = h.getMethodAnnotation(RequestMapping.class);
//获取当前请求接口中的name属性
String name = annotation.name();
//判断当前用户是否具有响应的请求权限
if(apis.contains(name)) {
request.setAttribute("user_claims",claims);
return true;
}else {
throw new CommonException(ResultCode.UNAUTHORISE);
}
}
}
throw new CommonException(ResultCode.UNAUTHENTICATED);
}
}
(2)修改UserController的profile方法
/**
*用户登录成功之后,获取用户信息
*1.获取用户id
*2.根据用户id查询用户
*3.构建返回值对象
*4.响应
*/
@RequestMapping(value="/profile",method = RequestMethod.POST)
public Result profile(HttpServletRequest request) throws Exception {
String userid = claims.getId();
//获取用户信息
User user = userService.findById(userid);
//根据不同的用户级别获取用户权限ProfileResult result = null;
if("user".equals(user.getLevel())) { result = new ProfileResult(user);
}else {
Map map = new HashMap(); if("coAdmin".equals(user.getLevel())) {
map.put("enVisible","1");
}
List<Permission> list = permissionService.findAll(map); result = new ProfileResult(user,list);
}
return new Result(ResultCode.SUCCESS,result);
}
(3)配置拦截器类,创建com.ihrm.system.SystemConfig
package com.ihrm.system;
import com.ihrm.common.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class SystemConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor). addPathPatterns("/**").
excludePathPatterns("/frame/login","/frame/register/**"); //设置不拦截的请求地址
}
}
以上代码会出现 no session 问题 解决方法:
package com.ihrm.system;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.common.utils.JwtUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
//1.配置springboot的包扫描
@SpringBootApplication(scanBasePackages = "com.ihrm")
//2.配置jpa注解的扫描
@EntityScan(value="com.ihrm.domain.system")
public class SystemApplication {
/**
* 启动方法
*/
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class,args);
}
@Bean
public IdWorker idWorker() {
return new IdWorker();
}
@Bean
public JwtUtils jwtUtils() {
return new JwtUtils();
}
//解决no session
@Bean
public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
return new OpenEntityManagerInViewFilter();
}
}