<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.6.0version>
dependency>
下面是返回结果,这里比较简单,我就不写注释了
public class ReturnResult {
private Object data;
private String code;
private String msg;
private ReturnResult( Object data, String code, String msg) {
this.data = data;
this.code = code;
this.msg = msg;
}
private ReturnResult(Object data) {
this.data = data;
this.code = ReturnMsg.SUCCESS.getCode();
this.msg = ReturnMsg.SUCCESS.getMsg();
}
private ReturnResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
private ReturnResult() {
this.code = ReturnMsg.ERROR.getCode();
this.msg = ReturnMsg.ERROR.getMsg();
}
public static ReturnResult success( Object data, String code, String msg) {
return new ReturnResult( data, code, msg);
}
public static ReturnResult success(Object data) {
return new ReturnResult(data);
}
public static ReturnResult success() {
return new ReturnResult("");
}
public static ReturnResult error() {
return new ReturnResult();
}
public static ReturnResult error(String code, String msg) {
return new ReturnResult(code, msg);
}
//getter和setter
}
下面是返回信息
public enum ReturnMsg {
SUCCESS("0", "操作成功"),
EXCEPTION1("10001", "未知异常"),
EXCEPTION2("10002", "操作失败"),
EXCEPTION3("10003", "上传失败"),
EXCEPTION4("10004", "账号或密码错误"),
EXCEPTION5("10005", "自定义异常"),
ERROR("-1", "失败");
private String code;
private String msg;
private ReturnMsg (String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
@RestController
@RequestMapping("user")
public class LoginController {
@Autowired
private JwtLoginService jwtLoginService;
@Autowired
//这里是查用户的详细信息,代码我就不贴了
private UserService userService;
@RequestMapping("login")
public ReturnResult login(@RequestBody Usr usr){
//这里会把生成的jwt返回
String jwt = jwtLoginService.login(usr);
//如果jwt为空,说明用户未登录,直接返回个错误
if (jwt==null||"".equals(jwt)){
return ReturnResult.error();
}
Usr usrvo = userService.menuByUserName(usr.getUsername());
//这里会把用户详情和jwt带给前端
return ReturnResult.success(usrvo,ReturnMsg.SUCCESS.getCode(),jwt) ;
}
}
public interface JwtLoginService {
String login(Usr userLogin);
}
@Service
public class JwtLoginServiceImpl implements JwtLoginService {
@Autowired
private UsrMapper usrMapper;
public String login(Usr userLogin){
//这里很简单的进行校验,如果能在数据库里查到传入的用户名和密码,说明登录成功,这里可以改成MD5加密校验
Usr usr = usrMapper.LoginByNamePass(userLogin.getUsername(), userLogin.getPassword());
//没查到说明登录失败
if(usr == null){
return null;
}else {
//登录成功 设置jwt
JWTUtils util = new JWTUtils();
//将查询到的角色ID存在一个数组里面写到jwt中,后面鉴权会用到
Map<String, Object> payload = new HashMap<String, Object>();
//这里我在Usr中封装了个List 可以使用Mybatis封装进去,下面我贴出UsrMapper的部分代码
List<Role> roleList = usr.getRoleList();
List<Long> roles = new ArrayList<>();
if (roleList!=null&&roleList.size()>0){
for (Role role : roleList) {
roles.add(role.getId());
}
}
payload.put("roles", roles);
try {
//86400000是过期时间 24小时
String jwt =util.createJWT("jwt", usr.getUsername(), 86400000,payload);
return jwt;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}
UsrMapper 这里大家自己来实现,也可以用SpringDataJPA
//登录根据用户名密码查找
@Select("SELECT * FROM usr WHERE username= #{username} AND PASSWORD = #{password} ")
@Results(value = {
@Result(id=true,property = "id",column = "id"),
@Result(property = "roleList",column = "id",many = @Many(select = "com.xxx.mapper.RoleMapper.findByUid"))
})
Usr LoginByNamePass(@Param("username") String username,@Param("password") String password);
public class JWTUtils {
public String createJWT(String id, String subject, long ttlMillis,Map<String, Object> claims) throws Exception {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
long nowMillis = System.currentTimeMillis();// 生成JWT的时间
Date now = new Date(nowMillis);
SecretKey key = generalKey();
// 下面就是在为payload添加各种标准声明和私有声明了
JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body
.setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setId(id) // 这里是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
.setIssuedAt(now) // jwt的签发时间
.setSubject(subject) // 这里代表这个JWT的主体,可以是用户名,id,保证唯一就可以了
.signWith(signatureAlgorithm, key);// 设置签名使用的签名算法和签名使用的秘钥
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp); // 设置过期时间
}
return builder.compact(); // 生成jwt
}
/**
* 解密jwt
*/
public Claims parseJWT(String jwt) throws Exception {
SecretKey key = generalKey(); // 签名秘钥,和生成的签名的秘钥一模一样
Claims claims = Jwts.parser() // 得到DefaultJwtParser
.setSigningKey(key) // 设置签名的秘钥
.parseClaimsJws(jwt).getBody();// 设置需要解析的jwt
return claims;
}
/**
* 由字符串生成加密key
*/
public SecretKey generalKey() {
PropertiesUtils propertiesUtils = new PropertiesUtils();
byte[] encodedKey = Base64.decodeBase64(“123”);// 本地的密码解码,这里自定义
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");// 根据给定的字节数组使用AES加密算法构造一个密钥,使用
return key;
}
}
@Component
public class JWTInterceptor implements HandlerInterceptor {
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
}
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
throws Exception {
}
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
JWTUtils util = new JWTUtils();
//前端请求必须在请求头中带Authorization
String jwt = request.getHeader("Authorization");
//获得realIP,用于白名单或黑名单,这是我配了Nginx,把真实的IP写到请求头里面了,这里可以不管
String realIP = request.getHeader("X-Real-IP");
if (realIP != null) {
if (realIP.equals("xxx.xxx.xxx.xxx"))) {
return true;
}
}
if (jwt == null) {
//这里应该用自定义异常
response.getWriter().write("未登录,请重新登录后操作");
return false;
} else {
Claims c = util.parseJWT(jwt);
//拿到自定义的角色数组
List<Integer> list = (List<Integer>) c.get("roles");
if (list == null || list.size() < 1) {
throw new InsufficientAuthorityException("非法用户");
}
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
//拿到方法上的注解,方法上的注解优先
XXSecurity eMethod = handlerMethod.getMethodAnnotation(XXSecurity .class);
//拿不到,也能写在了类上,下面做判断
if (eMethod != null) {
int[] value = eMethod.value();
//注解内容为空,说明这个方法不需要做权限控制
if (value.length == 0) {
return true;
}
//注解中的权限包含该用户的权限就直接放心
for (int i : value) {
if (list.contains(i)) {
return true;
}
}
//走完上面的循环,说明用户没有权限,我这里使用的自定义异常,当然return false也可以
throw new InsufficientAuthorityException("权限不足");
} else {
//拿到类上的注解,和上面同理
XXSecurity eType = handlerMethod.getMethod().getDeclaringClass().getAnnotation(XXSecurity .class);
if (eType != null) {
int[] value = eType.value();
if (value.length == 0) {
return true;
}
for (int i : value) {
if (list.contains(i)) {
return true;
}
}
//直接return false也行
throw new InsufficientAuthorityException("权限不足");
} else {
return true;
}
}
}
//走到这里说明在方法和类上都没有权限注解,直接放行
return true;
}
}
}
记得在springmvc.xml中配置
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<mvc:exclude-mapping path="/user/login.do" />
<bean class="com.xxx.jwt.interceptor.JWTInterceptor" />
mvc:interceptor>
mvc:interceptors>
/**
* 权限验证注解,可以加在类或方法上
* 方法上的优先
* 默认值是该方法所有人都可以访问
*/
//类和方法上生效
@Target({ElementType.METHOD,ElementType.TYPE})
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
//在javadoc文档中使用(这个不重要)
@Documented
public @interface XXSecurity {
int [] value() default {};
}
public class RoleConstant {
public static final int STU=1;
public static final int TEA=2;
public static final int EDU=3;
public static final int TEA_M=4;
}
类上方法上任意加,方法上优先
@RestController
@RequestMapping("user")
@XXSecurity(RoleConstant.STU)
public class UserController {
@Reference
private UserService userService;
@RequestMapping("menu")
@XXSecurity({RoleConstant.TEA,RoleConstant.STU})
public ReturnResult menu(String username) {
Usr usr = userService.menuByUserName(username);
return ReturnResult.success(usr);
}
}
第一次写博客,写的可能不太具体,有什么疑问可以加我qq 540814390,欢迎来撩,我也会不定期的更新,大家关注一下。