赞
踩
git仓库地址(代码可以直接下载使用):https://gitee.com/xu-kangyu/jwt-permission-verification
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
基本流程
1。用户输入账号密码,
2。进行验证返回给用户一个token,
3。用户访问一个接口
4。判断这个接口是否需要登录,不需要登录就直接访问,需要登录,在查看是否需要权限,
5.然后解析用户的token,查看用户是否有权限进行访问
yml还是连接数据库的那一套
建表
CREATE TABLE `user_account` (
`acc_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '账号id',
`username` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT '账号',
`password` varchar(128) COLLATE utf8_unicode_ci NOT NULL COMMENT '密码',
`pwd_key` char(8) COLLATE utf8_unicode_ci NOT NULL COMMENT '随机密码盐',
`role` char(2) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0' COMMENT '权限',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`acc_id`)
)
数据库
pom.xml
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.4.0</version>
</dependency>
自定义异常抛出类BaseException
package com.ly.jwt.utils; /** * 错误信息 * * @Author: 宇 * @catalogue: com.example.hotelmanagement.utils * @Date: 1999/9/9 9:99 * @Version: 1.0 */ public class BaseException extends RuntimeException { /** * 状态码 */ private String code; /** * 错误信息 */ private String errorMessage; public BaseException(String errorMessage) { super(errorMessage); this.code = "500"; this.errorMessage = errorMessage; } public BaseException(String code, String errorMessage) { super(errorMessage); this.code = code; this.errorMessage = errorMessage; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } }
自定义返回类JsonResult
package com.ly.jwt.utils; /** * 返回json调用 * * @Author: 宇 * @catalogue: com.example.hotelmanagement.utils * @Date: 1999/9/9 9:99 * @Version: 1.0 */ public class JsonResult<T> { /** * 返回码 */ private String code; /** * 返回信息 */ private String message; /** * 返回集 */ private T data; /** * 无参构造 */ public JsonResult() { } /** * 自定义返回类,查询方法 * * @param data */ public JsonResult(T data) { this.code = "200"; this.message = "操作成功"; this.data = data; } /** * @param message * @param data */ public JsonResult(String message, T data) { this.code = "200"; this.message = message; this.data = data; } /** * @param code * @param message * @param data */ public JsonResult(String code, String message, T data) { this.code = code; this.message = message; this.data = data; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
jwt生成token验证解析token的地方TokenUtil
package com.ly.jwt.utils; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import com.ly.jwt.db.entity.UserAccount; import org.apache.commons.lang3.StringUtils; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; /** * token生成 * * @Author: 宇 * @catalogue: com.example.hotelmanagement.utils * @Date: 1999/9/9 9:99 * @Version: 1.0 */ public class TokenUtil { /* 过期时间为24小时,毫秒计时的---毫秒--》秒--》分--》小时--》天 private static final long EXPIRE_TIME= 60 * 24 * 60 * 1000; */ private static final long EXPIRE_TIME = 60 * 1000 * 60 * 24; /** * 密钥,注意这里如果真实用到,应当设置到复杂点,相当于私钥的存在。如果被人拿到,相当于它可以自己制造token了。 */ private static final String TOKEN_SECRET = "token-ly"; //密钥盐 /** * 签名生成 * * @param userAccount * @return */ public static String sign(UserAccount userAccount) { String token = null; try { Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME); token = JWT.create() //存入token中,accId账号id,username用户名,role权限 .withClaim("accId", userAccount.getAccId()) .withClaim("username", userAccount.getUsername()) .withClaim("role", userAccount.getRole()) .withExpiresAt(expiresAt) // 使用了HMAC256加密算法。 .sign(Algorithm.HMAC256(TOKEN_SECRET)); } catch (Exception e) { e.printStackTrace(); } return token; } /** * 签名验证 * * @param token * @return */ public static boolean verify(String token) { if (StringUtils.isBlank(token)) { throw new BaseException("token为空"); } try { JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).build();//自定义的 DecodedJWT jwt = verifier.verify(token); System.out.println("认证通过:"); System.out.println("accId: " + jwt.getClaim("accId").asLong()); System.out.println("username: " + jwt.getClaim("username").asString()); System.out.println("role: " + jwt.getClaim("role").asString()); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("过期时间:" + sdf.format(jwt.getExpiresAt())); return true; } catch (Exception e) { return false; } } /** * <p> 获得accId,操作人 </p> * * @param request * @return int * @Description getIdByToken * @date 1999/9/9 9:99 */ public static Long getIdByToken(HttpServletRequest request) { String token = request.getHeader("token"); DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("accId").asLong(); } /** * <p> 获得用户名 </p> * * @param request * @return java.lang.String * @Description getUserNameByToken * @date 1999/9/9 9:99 */ public static String getUserNameByToken(HttpServletRequest request) { String token = request.getHeader("token"); DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("username").asString(); } /** * <p> 获得权限 </p> * * @param request * @return java.lang.String * @Description getRoleByToken * @date 1999/9/9 9:99 */ public static String getRoleByToken(HttpServletRequest request) { String token = request.getHeader("token"); DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("role").asString(); } }
验证token,进行拦截访问的TokenInterceptor
package com.ly.jwt.config; import com.alibaba.fastjson.JSONObject; import com.ly.jwt.utils.BaseException; import com.ly.jwt.utils.TokenUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; @Component public class TokenInterceptor implements HandlerInterceptor { /** * 没有封装那么多,只是类上可以写不需要登录。方法上就不用写了 * 方法上也可以写不需要登录,权限什么的在方法上面写,类上暂时没有写 * * @param request * @param response * @param handler * @return boolean * @Description preHandle * @date 1999/9/9 9:99 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException { //判断状态 if (request.getMethod().equals("OPTIONS")) { response.setStatus(HttpServletResponse.SC_OK); return true; } //设置编码 response.setCharacterEncoding("utf-8"); response.setContentType("application/json; charset=utf-8"); // 从 http 请求头中取出 token String token = request.getHeader("token"); //实例化 HandlerMethod handlerMethod = (HandlerMethod) handler; //返回此处理程序方法的方法,也就是取得接口的方法 Method method = handlerMethod.getMethod(); //查看类是否存在注释 if (method.getDeclaringClass().getAnnotation(RolePermission.class) != null) { //取得类注释 RolePermission a = method.getDeclaringClass().getAnnotation(RolePermission.class); //获取login的访问权限,是登录完访问,还是不登录就可以访问,false是不需要登录就可以访问 if (!a.login()) { return true; } } //查看接口是否存在注释 if (method.isAnnotationPresent(RolePermission.class)) { //取得接口的注解 RolePermission annotation = method.getAnnotation(RolePermission.class); //获取login的访问权限,是登录完访问,还是不登录就可以访问 //不需要登陆就可以访问 if (!annotation.login()) { return true; } } //if必须分开,避免方法没有注解,也需要进行普通的拦截 //判断token不为空,也可以不判断,因为验证的时候就进行了判断 if (!StringUtils.isBlank(token)) { //获取token的权限 String role = TokenUtil.getRoleByToken(request); //查看接口是否存在注释 if (method.isAnnotationPresent(RolePermission.class)) { //取得接口的注解 RolePermission annotation = method.getAnnotation(RolePermission.class); //获取接口需要的权限 String roleRolePermission = annotation.role(); if (StringUtils.isBlank(roleRolePermission)) { throw new BaseException("接口权限获取失败"); } //使用indexOf方法从头开始检索是否存在role,存在就返回位置,不存在就返回-1 int i = roleRolePermission.indexOf(role); if (i == -1) { throw new BaseException("用户权限不足"); } } //判断是否通过验证 boolean result = TokenUtil.verify(token); //通过拦截器 if (result) { return true; } } throw new BaseException("认证失败,未通过拦截器"); // try { // json.put("code", "50000"); // json.put("message", "认证失败,未通过拦截器"); // json.put("data", "false"); // response.getWriter().append(json.toJSONString()); // } catch (Exception e) { // e.printStackTrace(); // response.sendError(500); // return false; // } // return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
配置拦截的IntercepterConfig
package com.ly.jwt.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.ArrayList; import java.util.List; /** * 拦截器配置 */ @Configuration public class IntercepterConfig implements WebMvcConfigurer { private TokenInterceptor tokenInterceptor; /** * @param tokenInterceptor * @return * @introduction: 构造方法 * @date 2021/5/19 21:41 */ public IntercepterConfig(TokenInterceptor tokenInterceptor) { this.tokenInterceptor = tokenInterceptor; } /** * @param registry * @return void * @introduction: 设置拦截路径 * @date 2021/5/19 21:41 */ @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePath = new ArrayList<>(); //放开权限 excludePath.add("/user/login"); //登录 excludePath.add("/user/insertSelective"); //注册 excludePath.add("/static/**"); //静态资源 //注入拦截器 registry.addInterceptor(tokenInterceptor) .addPathPatterns("/**") // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录 // .excludePathPatterns("/user")//不拦截的 .excludePathPatterns(excludePath); WebMvcConfigurer.super.addInterceptors(registry); } /** * @param registry * @return void * @introduction: 跨域支持 * @date 2021/5/19 21:44 */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowCredentials(true) .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH", "OPTIONS", "HEAD") .maxAge(3600 * 24); } }
接收全局异常抛出类GlobalExceptionHandler
package com.ly.jwt.config; import com.ly.jwt.utils.BaseException; import com.ly.jwt.utils.JsonResult; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; /** * 本文只是没有系统的处理异常,一般应用系统中,我们都会定义多个异常类,来处理不同的异常情况。 * 然后通过异常处理类,处理不同种类的异常。 */ @RestControllerAdvice public class GlobalExceptionHandler { private Logger logger = LoggerFactory.getLogger(getClass()); /** * Spring的@ExceptionHandler可以用来统一处理方法抛出的异常, * 给这个方法加上@ExceptionHandler注解,这个方法就会处理类中其他方法(被@RequestMapping注解)抛出的异常。 * * @ExceptionHandler注解中可以添加参数,参数是某个异常类的class,代表这个方法专门处理该类异常 有些情况下我们会给标识了@RequestMapping的方法添加@ResponseBody,比如使用Ajax的场景,直接返回字符串, * 异常处理类也可以如此操作,添加@ResponseBody注解后,可以直接返回字符串, * @ExceptionHandler() * @ResponseBody */ //运行异常 @ExceptionHandler(value = {RuntimeException.class}) public JsonResult<?> RuntimeException(HttpServletRequest request, Exception e) { logger.error("400",e); return new JsonResult<>("400", e.getMessage(),"null"); } //空指针异常 @ExceptionHandler(value = {NullPointerException.class}) public JsonResult<?> NullPointerException(HttpServletRequest request, Exception e) { logger.error("404",e); return new JsonResult<>("404", e.getMessage(),"null"); } //好像是全局异常 @ExceptionHandler(value = {Exception.class}) public JsonResult<?> Exception(HttpServletRequest request, Exception e) { logger.error("500", e); return new JsonResult<>("500", e.getMessage(),"null"); } }
自定义权限注解RolePermission
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Target 说明了Annotation所修饰的对象范围:(指定下面的注解能修饰什么) Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)。在Annotation类型的声明中使用了target可更加明晰其修饰的目标。
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
package com.example.hotelmanagement.entity.model; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 权限注解 */ @Retention(RetentionPolicy.RUNTIME) //适用类、接口(包括注解类型)或枚举,适用方法 @Target({ElementType.TYPE, ElementType.METHOD}) //@interface自定义注解 public @interface RolePermission { /** * @return boolean * @introduction: 登录权限 * @date 2021/5/19 22:23 */ boolean login() default true; /** * @return java.lang.String * @introduction: 角色权限 * @date 2021/5/19 22:24 */ String role() default ""; }
自定义权限组AuthorityUtils
package com.ly.jwt.utils; /** * 权限包 * * @Author: 宇 * @catalogue: com.example.hotelmanagement.utils * @Date: 1999/9/9 9:99 * @Version: 1.0 */ public class AuthorityUtils { /** * 权限可以自定义,因为有最高权限,所以其他的人也可以进行访问 * 也可以定义其他的权限互不干扰,例如管理人的就不能管理器材 */ //最高权限,管理的 public static final String ADMIN1 = "1"; //下级权限2,管理人得 public static final String USER2 = "1,2"; //下级权限3,管理器材的 public static final String USER3 = "1,3"; }
controller,登录访问测试接口
package com.ly.jwt.controller; import com.ly.jwt.config.RolePermission; import com.ly.jwt.db.entity.UserAccount; import com.ly.jwt.service.UserAccountService; import com.ly.jwt.utils.AuthorityUtils; import com.ly.jwt.utils.JsonResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.util.List; /** * 账号的 */ @RestController @RequestMapping("/user") public class UserAccountController { @Autowired UserAccountService userAccountService; @PostMapping(value = "/login") public JsonResult<?> login(@RequestBody UserAccount userAccount) { return userAccountService.login(userAccount); } @PostMapping(value = "/insertSelective") public JsonResult<?> insertSelective(@RequestBody UserAccount userAccount) { return userAccountService.insertSelective(userAccount); } @RolePermission(login = false) @PostMapping(value = "/selectAll") public JsonResult<?> selectAll(HttpServletRequest request) { List<UserAccount> userAccounts = userAccountService.selectAll(); return new JsonResult<>(userAccounts); } @PostMapping(value = "/selectAll1") public JsonResult<?> selectAll1(HttpServletRequest request) { List<UserAccount> userAccounts = userAccountService.selectAll(); return new JsonResult<>(userAccounts); } @RolePermission(role = AuthorityUtils.USER2) @PostMapping(value = "/selectAll2") public JsonResult<?> selectAll2(HttpServletRequest request) { List<UserAccount> userAccounts = userAccountService.selectAll(); return new JsonResult<>(userAccounts); } }
实体类entity
package com.ly.jwt.db.entity; import java.util.Date; public class UserAccount { /** * 账号id */ private Long accId; /** * 账号 */ private String username; /** * 密码 */ private String password; /** * 随机密码盐 */ private String pwdKey; /** * 权限 */ private String role; /** * 创建时间 */ private Date createTime; //省略set,get方法 }
service
package com.ly.jwt.service; import com.ly.jwt.db.entity.UserAccount; import com.ly.jwt.utils.JsonResult; import java.util.List; public interface UserAccountService { //登录 JsonResult<?> login(UserAccount userAccount); //查询全部 List<UserAccount> selectAll(); //添加账号 JsonResult<?> insertSelective(UserAccount userAccount); }
serviceImpl
package com.ly.jwt.service.impl; import cn.hutool.core.util.ObjectUtil; import com.ly.jwt.db.entity.UserAccount; import com.ly.jwt.db.mapper.UserAccountMapper; import com.ly.jwt.service.UserAccountService; import com.ly.jwt.utils.BaseException; import com.ly.jwt.utils.JsonResult; import com.ly.jwt.utils.PwdTool; import com.ly.jwt.utils.TokenUtil; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; import java.util.List; @Service public class UserAccountServiceImpl implements UserAccountService { @Autowired private UserAccountMapper userAccountMapper; //登录 @Override public JsonResult<?> login(UserAccount userAccount) { //传入对象 if (userAccount == null) { throw new NullPointerException("传入对象为空"); } //账号 String username = userAccount.getUsername(); if (StringUtils.isBlank(username)) { throw new NullPointerException("账号为空"); } //根据用户名查询信息==>密码盐 UserAccount userAccount1 = userAccountMapper.selectByUsername(username); //密码 String password = userAccount.getPassword(); if (StringUtils.isBlank(password)) { throw new NullPointerException("密码为空"); } //用户输入密码,和根据输入用户名从数据库进行寻找密码盐 String password1 = PwdTool.getHMAC(password, userAccount1.getPwdKey()); //赋值根据密码盐加密后的密码 userAccount.setPassword(password1); //高根据用户名和密码进行数据库比对查询 UserAccount userAccount2 = userAccountMapper.login(userAccount); //如果不为空就生成token if (ObjectUtil.isEmpty(userAccount2)) { throw new BaseException("用户名或密码错误"); } //生成token String token = TokenUtil.sign(userAccount1); if (StringUtils.isBlank(token)) { throw new BaseException("token生成失败"); } return new JsonResult<>(token); } //查询全部 @Override public List<UserAccount> selectAll() { return userAccountMapper.selectAll(); } //添加账号 @Override public JsonResult<?> insertSelective(UserAccount userAccount) { //传入对象 if (ObjectUtil.isEmpty(userAccount)) { throw new BaseException("未获取到参数"); } //账号 String username = userAccount.getUsername(); if (ObjectUtil.isEmpty(username)) { throw new BaseException("未获取到username"); } //根据用户名进行查询,不允许重复 UserAccount userAccount1 = userAccountMapper.selectByUsername(username); if (!ObjectUtil.isEmpty(userAccount1)) { throw new BaseException("用户名重复"); } //密码 String password = userAccount.getPassword(); if (ObjectUtil.isEmpty(password)) { throw new BaseException("未获取到password"); } //密码盐==>调用工具类进行获取 String pwdKey = PwdTool.getRandomSalt(); userAccount.setPwdKey(pwdKey); //使用密码盐加密后的密码 String password1 = PwdTool.getHMAC(password, pwdKey); //赋值新密码 userAccount.setPassword(password1); //创建时间 userAccount.setCreateTime(new Date()); int i = userAccountMapper.insertSelective(userAccount); if (i != 1) { throw new BaseException("添加失败"); } return new JsonResult<>("添加成功"); } }
mapper
package com.ly.jwt.db.mapper; import com.ly.jwt.db.entity.UserAccount; import java.util.List; public interface UserAccountMapper { //注册 int insertSelective(UserAccount record); //登录 UserAccount login(UserAccount userAccount); //查询全部 List<UserAccount> selectAll(); //根据用户名查询 UserAccount selectByUsername(String username); }
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.example.hotelmanagement.mapper.UserMapper"> <resultMap id="BaseResultMap" type="com.example.hotelmanagement.entity.User"> <id column="id" jdbcType="BIGINT" property="id" /> <result column="username" jdbcType="VARCHAR" property="username" /> <result column="password" jdbcType="VARCHAR" property="password" /> <result column="role" jdbcType="VARCHAR" property="role" /> <result column="status" jdbcType="INTEGER" property="status" /> </resultMap> <sql id="Base_Column_List"> acc_id, username, password, pwd_key, role, create_time </sql> <insert id="insertSelective" parameterType="com.ly.jwt.db.entity.UserAccount"> insert into user_account <trim prefix="(" suffix=")" suffixOverrides=","> <if test="accId != null"> acc_id, </if> <if test="username != null"> username, </if> <if test="password != null"> password, </if> <if test="pwdKey != null"> pwd_key, </if> <if test="role != null"> role, </if> <if test="createTime != null"> create_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="accId != null"> #{accId,jdbcType=BIGINT}, </if> <if test="username != null"> #{username,jdbcType=VARCHAR}, </if> <if test="password != null"> #{password,jdbcType=VARCHAR}, </if> <if test="pwdKey != null"> #{pwdKey,jdbcType=CHAR}, </if> <if test="role != null"> #{role,jdbcType=CHAR}, </if> <if test="createTime != null"> #{createTime,jdbcType=TIMESTAMP}, </if> </trim> </insert> <!-- 查询全部--> <select id="selectAll" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user_account </select> <!-- 根据用户名查询--> <select id="selectByUsername" parameterType="string" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from user_account where username=#{username} </select> <!-- 登录--> <select id="login" resultMap="BaseResultMap"> select * from user_account where username=#{username} and password = #{password} </select> </mapper>
1。不登陆直接访问,只能访问没有权限的
2.登录没有权限的进行访问,会提示权限不足
3。登录有权限的全都可以访问
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。