前言
本项目采用SpringBoot+SpringSecurity+Jwt+Mybatis,实现了基于数据库的用户认证以及权限分配。
pom.xml导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--自定义的处理类中需要用到-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--注解配置处理器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--Swagger相关依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
</dependencies>
数据库表结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` int NOT NULL AUTO_INCREMENT,
`pattern` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '请求路径匹配规则',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `menu` VALUES (1, '/db/**');
INSERT INTO `menu` VALUES (2, '/admin/**');
INSERT INTO `menu` VALUES (3, '/user/**');
DROP TABLE IF EXISTS `menu_role`;
CREATE TABLE `menu_role` (
`id` int NOT NULL AUTO_INCREMENT,
`mid` int NOT NULL COMMENT 'menu表外键',
`rid` int NOT NULL COMMENT 'role表外键',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `menu_role` VALUES (1, 1, 1);
INSERT INTO `menu_role` VALUES (2, 2, 2);
INSERT INTO `menu_role` VALUES (3, 3, 3);
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`nameZh` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `role` VALUES (1, 'dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'user', '用户');
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`enabled` tinyint(1) NULL DEFAULT NULL,
`locked` tinyint(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `user` VALUES (1, 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', 1, 0);
INSERT INTO `user` VALUES (2, 'admin', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', 1, 0);
INSERT INTO `user` VALUES (3, 'user', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', 1, 0);
INSERT INTO `user` VALUES (4, 'Sabrina', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', 1, 1);
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`id` int NOT NULL AUTO_INCREMENT,
`uid` int NULL DEFAULT NULL,
`rid` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;
INSERT INTO `user_role` VALUES (1, 1, 1);
INSERT INTO `user_role` VALUES (2, 1, 2);
INSERT INTO `user_role` VALUES (3, 2, 2);
INSERT INTO `user_role` VALUES (4, 3, 3);
SET FOREIGN_KEY_CHECKS = 1;
项目结构图
核心配置类SecurityConfig
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
JwtUserDetailService userService;
@Autowired
PermissionFilter permissionFilter;
@Autowired
UserAccessDecisionManager userAccessDecisionManager;
@Bean
PermissionFilter permissionFilter()
{
return new PermissionFilter();
}
@Bean
PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>()
{
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o)
{
o.setAccessDecisionManager(userAccessDecisionManager);
o.setSecurityMetadataSource(permissionFilter);
return o;
}
})
.antMatchers(
HttpMethod.GET,
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/webSocket/**"
).permitAll()
.antMatchers("/file/**").permitAll()
.antMatchers("/druid/**").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers(HttpMethod.POST,"/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtLoginFilter("/login",
authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JwtTokenFilter(),UsernamePasswordAuthenticationFilter.class)
.cors().and().csrf().disable();
}
@Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring()
.antMatchers("/swagger-ui.html","v2/api-docs")
.antMatchers("/v2/**")
.antMatchers("/swagger-resources/**")
.antMatchers("/webjars/**")
.antMatchers("/*/api-docs")
.antMatchers("/doc.html");
}
}
实体类
public class JwtUserDetails implements UserDetails
{
private Integer id;
private String username;
private String password;
private Boolean enabled;
private Boolean locked;
private List<Role> roles;
private String token;
@Override
public Collection<? extends GrantedAuthority> getAuthorities()
{
return roles == null ? new ArrayList<SimpleGrantedAuthority>()
: roles.stream().map(role -> new SimpleGrantedAuthority(String.valueOf(role.getRoleId()))).collect(Collectors.toList());
}
@Override
public String getPassword()
{
return this.password;
}
@Override
public String getUsername()
{
return this.password;
}
@Override
public boolean isAccountNonExpired()
{
return true;
}
@Override
public boolean isAccountNonLocked()
{
return !locked;
}
@Override
public boolean isCredentialsNonExpired()
{
return true;
}
@Override
public boolean isEnabled()
{
return enabled;
}
public Integer getId()
{
return id;
}
public void setId(Integer id)
{
this.id = id;
}
public Boolean getEnabled()
{
return enabled;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public Boolean getLocked()
{
return locked;
}
public void setLocked(Boolean locked)
{
this.locked = locked;
}
public String getToken()
{
return token;
}
public void setToken(String token)
{
this.token = token;
}
public List<Role> getRoles()
{
return roles;
}
public void setRoles(List<Role> roles)
{
this.roles = roles;
}
}
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Menu
{
private Integer id;
private String pattern;
private List<Role> roles;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role
{
private Integer roleId;
private String name;
private String nameZh;
}
工具类
@Component
@Slf4j
public class JwtTokenUtil
{
public final static String CLAIMS_KEY_AUTHORITIES = "authorities";
public final static String SECRET = "Turing-Team";
public final static Long TOKEN_VALIDITY_IN_SECONDS = 600000000L;
public void printLogger(Exception e)
{
log.error("Error:{}",e.getMessage());
}
public Claims getClaimsFromToken(String token)
{
Claims claims;
try
{
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
}catch (ExpiredJwtException e)
{
printLogger(e);
claims = null;
}catch (IncorrectClaimException e)
{
printLogger(e);
claims = null;
}
catch (Exception e)
{
printLogger(e);
claims = null;
}
return claims;
}
public String getUsernameFromToken(String token)
{
String username;
try
{
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
}catch (Exception e)
{
printLogger(e);
username = null;
}
return username;
}
public Date getCreateDateFromToken(String token)
{
Date date;
try
{
Claims claims = getClaimsFromToken(token);
date = claims.getIssuedAt();
}catch (Exception e)
{
date = null;
printLogger(e);
}
return date;
}
public Date getExpirationDateFromToken(String token)
{
Date date;
try
{
Claims claims = getClaimsFromToken(token);
date = claims.getExpiration();
}
catch (Exception e)
{
printLogger(e);
date = null;
}
return date;
}
private Date generateExpirationDate()
{
return new Date(System.currentTimeMillis() + TOKEN_VALIDITY_IN_SECONDS);
}
public boolean isTokenExpired(String token)
{
Date date = getExpirationDateFromToken(token);
return getClaimsFromToken(token) == null || date.before(new Date());
}
public String generateToken(String username,StringBuffer roles)
{
Claims claims = new DefaultClaims();
claims.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(generateExpirationDate());
return Jwts.builder()
.setClaims(claims)
.claim(CLAIMS_KEY_AUTHORITIES,roles)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public String refreshToken(String token)
{
String refreshedToken;
try
{
Claims claims = getClaimsFromToken(token);
StringBuffer roles = (StringBuffer) claims.get(CLAIMS_KEY_AUTHORITIES);
refreshedToken = generateToken(claims.getSubject(),roles);
}catch (Exception e)
{
printLogger(e);
refreshedToken = null;
}
return refreshedToken;
}
public boolean validateToken(String token, UserDetails userDetails)
{
JwtUserDetails user = (JwtUserDetails) userDetails;
String username = getUsernameFromToken(token);
return username.equals(user.getUsername()) && !isTokenExpired(token);
}
public Jws<Claims> parserToken(String token)throws JwtException
{
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token);
}
}
@Component
public class SpringInjectUtil implements ApplicationContextAware
{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if(SpringInjectUtil.applicationContext == null){
SpringInjectUtil.applicationContext = applicationContext;
}
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
public static Object getBean(String name){
return getApplicationContext().getBean(name);
}
public static <T> T getBean(Class<T> clazz){
return getApplicationContext().getBean(clazz);
}
public static <T> T getBean(String name,Class<T> clazz){
return getApplicationContext().getBean(name,clazz);
}
}
@Data
@Configuration()
@ConfigurationProperties(prefix = "jwt")
public class JwtSecurityProperties
{
private String header;
private String secret;
private Long tokenValidityInSeconds;
}
jwt:
header: Authorization
secret: Turing-Team
token_validity-in-seconds: 600000000
用户登录认证
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter
{
@Resource
JwtTokenUtil jwtTokenUtil;
private void init()
{
if (jwtTokenUtil == null)
{
jwtTokenUtil = (JwtTokenUtil) SpringInjectUtil.getBean("jwtTokenUtil");
}
}
public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager)
{
super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
init();
setAuthenticationManager(authenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException
{
String username = httpServletRequest.getParameter("username");
String password = httpServletRequest.getParameter("password");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
return getAuthenticationManager().authenticate(token);
}
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException
{
response.setContentType("application/json;charset=utf-8");
Result<JwtUserDetails> result = new Result<>();
PrintWriter out = response.getWriter();
String responseResult;
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
StringBuffer roles = new StringBuffer();
String username = request.getParameter("username");
JwtUserDetails user = (JwtUserDetails) authResult.getPrincipal();
List<Role> rolesList = user.getRoles();
for (Role role : rolesList)
{
if (role != null)
{
System.out.println(role);
roles.append(role.getRoleId()+",");
}
}
String token = jwtTokenUtil.generateToken(username, roles);
user.setToken(token);
result.code(ResultCode.SUCCESS).message("Login Success").data(user);
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException
{
response.setContentType("application/json;charset=utf-8");
PrintWriter out =response.getWriter();
Result<String> result = new Result<>();
if (failed instanceof LockedException)
{
result.code(ResultCode.ERROR).message("账号已被锁定,无法进行登录操作!").data("Account had been locked");
}
else if (failed instanceof DisabledException)
{
result.code(ResultCode.ERROR).message("账号不可用,无法进行登录操作!").data("Account is disabled");
}else
{
result.message("用户名或者密码错误,请重新登录!").code(ResultCode.ERROR).data("Login Failure");
}
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
}
}
Token令牌验证
public class JwtTokenFilter extends GenericFilterBean
{
private static JwtTokenFilter jwtTokenFilter;
@Resource
JwtSecurityProperties jwtSecurityProperties;
@Resource
JwtTokenUtil jwtTokenUtil;
private void init()
{
if (jwtSecurityProperties == null)
{
jwtSecurityProperties = (JwtSecurityProperties) SpringInjectUtil.getBean("jwtSecurityProperties");
}
if (jwtTokenUtil == null)
{
jwtTokenUtil = (JwtTokenUtil) SpringInjectUtil.getBean("jwtTokenUtil");
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException
{
init();
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String token = request.getHeader(jwtSecurityProperties.getHeader());
PrintWriter out;
Result result;
response.setContentType("application/json;charset=utf-8");
if ("".equals(token) || token == null)
{
out = response.getWriter();
result = new Result<String>();
result.code(ResultCode.UNAUTHORIZED)
.message("必须携带用户的认证信息进行访问")
.data("User Unauthorized");
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
return;
}
if (jwtTokenUtil.isTokenExpired(token))
{
out = response.getWriter();
result = new Result<String>();
result.code(ResultCode.UNAUTHORIZED)
.message("登录认证已过期,请重新登录!")
.data("User Authorization Is Expired");
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
return;
}
Jws<Claims> jws;
try
{
jws = jwtTokenUtil.parserToken(token);
}catch (JwtException e)
{
out = response.getWriter();
result = new Result<String>();
result.code(ResultCode.UNAUTHORIZED)
.message("登录认证无效!")
.data("Invalid Authorization ");
out.write(new ObjectMapper().writeValueAsString(result));
out.flush();
out.close();
return;
}
Claims user = jws.getBody();
String username = user.getSubject();
String authoritiesString = (String) user.get(JwtTokenUtil.CLAIMS_KEY_AUTHORITIES);
List<GrantedAuthority> authorities =
AuthorityUtils.commaSeparatedStringToAuthorityList(authoritiesString);
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(servletRequest, servletResponse);
}
}
获取用户权限
public class PermissionFilter implements FilterInvocationSecurityMetadataSource
{
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
MenuService menuService;
private void init()
{
if (menuService == null)
{
menuService = (MenuService) SpringInjectUtil.getBean("menuService");
}
}
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException
{
init();
FilterInvocation filterInvocation = (FilterInvocation) o;
String requestUrl = filterInvocation.getRequestUrl();
List<Menu> menus = menuService.findAllMenusWithRoles();
for (Menu menu : menus)
{
if (antPathMatcher.match(menu.getPattern(),requestUrl))
{
List<Role> roles = menu.getRoles();
String[] rolesArray = new String[roles.size()];
for (int i = 0; i < roles.size(); i++)
{
rolesArray[i] = String.valueOf(roles.get(i).getRoleId());
}
return SecurityConfig.createList(rolesArray);
}
}
return SecurityConfig.createList("Login-Common");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes()
{
return null;
}
@Override
public boolean supports(Class<?> aClass)
{
return true;
}
}
用户权限验证
@Component
public class UserAccessDecisionManager implements AccessDecisionManager
{
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException
{
for (ConfigAttribute configAttribute : collection)
{
if ("Login-Common".equals(configAttribute.getAttribute()))
{
if (authentication instanceof AnonymousAuthenticationToken)
{
throw new AccessDeniedException("尚未登陆,请前往登录!");
}
return;
}
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (GrantedAuthority authority : authorities)
{
if (authority.getAuthority().equals(configAttribute.getAttribute()))
{
return;
}
}
}
throw new AccessDeniedException("权限不足,请联系管理员!");
}
@Override
public boolean supports(ConfigAttribute configAttribute)
{
return true;
}
@Override
public boolean supports(Class<?> aClass)
{
return true;
}
}
Service层实现类
@Service
public class JwtUserDetailService implements UserDetailsService
{
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
JwtUserDetails jwtUserDetails = userMapper.loadUserByUsername(username);
if (jwtUserDetails == null)
{
throw new UsernameNotFoundException("用户不存在");
}
jwtUserDetails.setRoles(roleMapper.findRolesByUserId(jwtUserDetails.getId()));
return jwtUserDetails;
}
}
@Service
public class MenuServiceImpl implements MenuService
{
@Autowired
MenuMapper menuMapper;
@Override
public List<Menu> findAllMenusWithRoles()
{
return menuMapper.findAllMenuWithRoles();
}
}
@Service
public class RoleServiceImpl implements RoleService
{
@Autowired
JwtUserDetailService userDetailService;
@Autowired
RoleMapper roleMapper;
@Override
public Role findRolesByUsername(String username)
{
JwtUserDetails user = (JwtUserDetails) userDetailService.loadUserByUsername(username);
return roleMapper.findRoleByUserId(user.getId());
}
}
统一响应类
public class Result<T>
{
private Integer code;
private String message;
private T data;
public Result() {}
public Result message(String message)
{
this.message = message;
return this;
}
public Result code(Integer code)
{
this.code = code;
return this;
}
public Result data(T data)
{
this.data = data;
return this;
}
}
Controller
@RestController
@RequestMapping
public class UserController
{
@GetMapping("/hello")
public Result hello()
{
return new Result().message("访问公共接口成功").code(ResultCode.SUCCESS).data("Success");
}
@GetMapping("/db/hello")
public Result db()
{
return new Result().message("访问dba角色接口成功").code(ResultCode.SUCCESS).data("Success");
}
@GetMapping("/admin/hello")
public Result admin()
{
return new Result().message("访问admin接口成功").code(ResultCode.SUCCESS).data("Success");
}
@GetMapping("/user/hello")
public Result user()
{
return new Result().message("访问user接口成功").code(ResultCode.SUCCESS).data("Success");
}
}
流程解析
- 用户发起登录请求之后,会被
JwtLoginFilter 所拦截,进行登录信息验证;登录成功之后会返回用户信息,并且让用户的之后发送来的请求头中携带生成的Token 。 - 用户发起非登录请求之后,会被
JwtTokenFilter 所拦截,解析并验证请求中所带的Token ,判断是否处于已登录状态。 - 已登陆的情况下,会继续被
PermissionFilter 所拦截,判断用户此次访问请求需要具备哪些角色,然后将其封装起来发送至请求访问管理类UserAccessDecisionManager 。 - 请求访问管理类
UserAccessDecisionManager 得到信息后,判断此次访问是否合法,如果合法则放行,否则将抛出异常。
测试
登录测试
登录成功:
账户被锁定:
账户不可用:
账户不存在:
公共接口测试
访问具有访问权限的接口
数据库管理员 :
管理员:
访问无访问权限的接口
用户:
以上,整个登录认证以及权限控制功能已经搭建好,可以在此基础上根据需求添加其他的业务。
该文仅作为本人学习使用,水平与能力有限,如有错误或不足欢迎指正!
|