SpringSecurity的简单使用(使用JWT验证)

229 阅读9分钟

前言

SpringSecurity是一个功能强大且高度可定制的身份验证和访问控制的框架。

提供完善的认证机制和方法级的授权功能

它的核心是一组过滤链,不同功能经由不同的过滤器。

image.png


JWT介绍

JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。

JWT组成

格式

JWT token

  • header
  • payload
  • signature

header

header中存放签名的生成算法

{"alg": "HS512"}

payload

payload中存放用户名、token的生成时间和过期时间

{"sub":"admin","created":1489079981393,"exp":1489684781}

signature

signature为以header和payload生成的签名,一旦header和payload被篡改,验证则失败

//secret为加密算法的密钥
String signature = HMACSHA512(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)

样例

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbiIsImNyZWF0ZWQiOjE1NTY3NzkxMjUzMDksImV4cCI6MTU1NzM4MzkyNX0.diki0193X0bBOETf2UN3r3PotNIEAV7mzIxxeI5IxFyzzkOZxS0PGfF_SK6wxCv2K8S0cZjMkv6b5bCqc0VBw

认证与授权原理

  • 用户登录,成功后返回Token
  • 登录后每次用户在调用接口时在header中添加Authorization的头,值为token
  • 后台对request中Authorization信息解析校验获得用户信息,实现认证和授权

依赖

<!--JWT登录支持-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

工具类

在这里原作者将工具类交由了Spring管理,全局仅存在一个Utils

/**
 * JwtToken生成工具类
 */
@Component
public class JwtTokenUtils {
    //Token日志
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtils.class);

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_CREATED = "created";

    //Jwt生成密钥
    @Value("${jwt.secret}")
    private String secret;

    //Jwt过期时间
    @Value("${jwt.expiration}")
    private Long expiration;

    /**
     *返回token过期时间
     * @return  token过期时间
     */
    private Date generateExpirationDate(){
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    /**
     * 根据Claims生成JwtToken
     * @param claims token附带的信息
     * @return       token字符串
     */
    private String generateToken(Map<String,Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512,secret)
                .compact();
    }

    /**
     *从token获取JWT中的负载
     * @param token token字符串
     * @return  JWT负载
     */
    private Claims getClaimsFromToken(String token){
        Claims claims = Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        if (claims == null){
            LOGGER.info("JWT格式验证失败:{}",token);
        }
        return claims;
    }

    /**
     *根据token字符串解析返回username
     * @param token token字符串
     * @return      username
     */
    public String getUsernameFromToken(String token){
        Claims claims = getClaimsFromToken(token);
        String username = claims.getSubject();
        return username;
    }

    /**
     * 验证Token是否有效
     * @param token         Token字符串
     * @param userDetails   数据库中用户信息
     * @return              是否有效
     */
    public boolean validateToken(String token, UserDetails userDetails){
        String username = getUsernameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
    }

    /**
     * 根据Token判断是否已经失效
     * @param token     token字符串
     * @return          token是否失效
     */
    private boolean isTokenExpired(String token){
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /**
     * 根据token获取过期时间
     * @param token token字符串
     * @return      token过期时间
     */
    private Date getExpiredDateFromToken(String token){
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 根据用户信息生成Token
     * @param userDetails   用户信息
     * @return              token字符串
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 判断token是否可以刷新
     * @param token Token字符串
     * @return      是否可以刷新
     */
    public boolean canRefresh(String token){
        //没有过期的Token才可以被刷新
        return !isTokenExpired(token);
    }

    /**
     * 刷新token
     * @param token token字符串
     * @return      新的Token
     */
    public String refreshToken(String token){
        Claims claims = getClaimsFromToken(token);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }
}

SpringSecurity

依赖

<!--SpringSecurity依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

使用

SpringSecurity配置类

自定义SpringSecurity配置 extends WebSecurityConfigurerAdapter

否则会自动注入DefaultConfigurerAdapter类----The default configuration for web security

/**
 * SpringSecurity配置类
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private UmsAdminService umsAdminService;
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()     //使用Jwt,不需要csrf
                .disable()
                .sessionManagement()    //基于Token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(    //允许对网站静态资源的无授权访问
                        "/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-ui/**",
                        "/swagger-resources/**",
                        "/v3/api-docs/**",
                        "/v2/api-docs/**"
                ).permitAll()
                .antMatchers("/admin/login","/admin/register").permitAll() //对登录注册允许匿名访问
                .antMatchers(HttpMethod.OPTIONS).permitAll()    //跨域请求会进行一次options请求
//                .antMatchers("/**").permitAll()               //测试时全部允许访问
                .anyRequest()   //除上面外的所有请求全部需要鉴权认证
                .authenticated();
        //禁用缓存
        httpSecurity.headers().cacheControl();
        //添加Jwt Filter
        httpSecurity.addFilterBefore(jwtAuthentucationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    /**
     * 配置UserDetailsService PasswordEncoder
     */
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    //配置UserDetailsService
    public UserDetailsService userDetailsService(){
        //获取用户登录信息
        return username -> {
            //根据username查询用户
            UmsAdmin adminByUsername = umsAdminService.getAdminByUsername(username);
            if (Objects.nonNull(adminByUsername)){
                //根据userId获得PermissioonList
                List<UmsPermission> permissionList = umsAdminService.getPermissionList(adminByUsername.getId());
                return new AdminUserDetails(adminByUsername, permissionList);
            }
            throw new UsernameNotFoundException("用户名或密码错误");
        };
    }

    /**
     * Jwt过滤器
     */
    @Bean
    public JwtAuthentucationTokenFilter jwtAuthentucationTokenFilter(){
        JwtAuthentucationTokenFilter jwtAuthentucationTokenFilter = new JwtAuthentucationTokenFilter();
        return jwtAuthentucationTokenFilter;
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}
  • void configure(HttpSecurity httpSecurity)
    
    • httpSecurity.antMatchers(    //允许对网站静态资源的无授权访问
              "/",
              "/*.html",
              "/favicon.ico",
              "/**/*.html",
              "/**/*.css",
              "/**/*.js",
              "/swagger-ui/**",
              "/swagger-resources/**",
              "/v3/api-docs/**",		//Swagger3 ...?位置
              "/v2/api-docs/**"    
      ).permitAll()	
      
    • //跨域请求会进行一次options请求 -- 允许
      httpSecurity.antMatchers(HttpMethod.OPTIONS).permitAll()   
      
    • //添加Jwt Filter 注入的为下方@Bean注入的对象
      httpSecurity.addFilterBefore(jwtAuthentucationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
      
    • //添加自定义未授权和未登录结果返回 response直接返回 在后面了
      httpSecurity.exceptionHandling()
              .accessDeniedHandler(restfulAccessDeniedHandler)
              .authenticationEntryPoint(restAuthenticationEntryPoint);
      
  •   @Override
      /**
       * 配置UserDetailsService
       * 配置PasswordEncoder
       */
      protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          //注入的为下方@Bean注入的对象
          auth.userDetailsService(userDetailsService())
              .passwordEncoder(passwordEncoder());
      }
    

PasswordEncoder

在这里直接在SpringSecurity类内注入

//注入默认
//可以对密码进行加密 PasswordEncoder.encode(CharSequence rawPassword);
//可以用明文密码与加密密码进行对比
//PasswordEncoder.matches(CharSequence rawPassword, String encodedPassword);
@Bean
public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

UserDetailsService

直接在SpringSecurity类内注入了,因为只有一个接口,可以通过Lambda重写。

```java
//重写了UserDetailsService.loadUserByUsername(String username);
@Bean
//配置UserDetailsService
public UserDetailsService userDetailsService(){
    //获取用户登录信息
    return username -> {
        //根据username查询用户
        UmsAdmin adminByUsername = umsAdminService.getAdminByUsername(username);
        if (Objects.nonNull(adminByUsername)){
            //根据userId获得PermissioonList
            List<UmsPermission> permissionList = umsAdminService.getPermissionList(adminByUsername.getId());
            //AdminUserDetails implements UserDetails
            //重写了UserDetails接口方法
            return new AdminUserDetails(adminByUsername, permissionList);
        }
        throw new UsernameNotFoundException("用户名或密码错误");
    };
}
```

UserDetails实现类

UserDetails规定了SpringSecurity需要的用户信息的方法,我们要对其进行重写

在这个项目中许多权限并没有用到,所以直接返回为true了

/**
 * SpringSecurity需要的用户详情
 */
public class AdminUserDetails implements UserDetails {
    //用户实体
    private UmsAdmin umsAdmin;
    //用户权限实体List
    private List<UmsPermission> permissionList;
	
    /**
     * AdminUserDetails构造方法
     * @param umsAdmin          用户实体
     * @param permissionList    用户权限信息List
     */
    public AdminUserDetails(UmsAdmin umsAdmin, List<UmsPermission> permissionList) {
        this.umsAdmin = umsAdmin;
        this.permissionList = permissionList;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //返回当前用户的权限对象SimpleGrantedAuthority List 
        //SimpleGrantedAuthority内仅有属性role对应权限内容的字段
        return permissionList
                .stream().filter(permission -> Objects.nonNull(permission.getValue()))
                .map(permission -> new SimpleGrantedAuthority(permission.getValue()))
                .collect(Collectors.toList());
    }

    @Override
    public String getPassword() {
        return umsAdmin.getPassword();
    }

    @Override
    public String getUsername() {
        return umsAdmin.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        //账号未过期
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        //账号未锁定
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        //凭证是否 未过期
        return true;
    }

    @Override
    public boolean isEnabled() {
        //是否启用
        return umsAdmin.getStatus().equals(1);
    }
}

UmsAdminService

用到的对用户信息查询的Service接口,具体实现与Dao层不再展示

package com.yancy0109.mall.service;

import com.yancy0109.mall.mbg.model.UmsAdmin;
import com.yancy0109.mall.mbg.model.UmsPermission;

import java.util.List;

/**
 * 后台管理员Service
 */
public interface UmsAdminService {

    /**
     * 根据用户名获取后台管理员
     * @param username  用户名
     * @return          UmsAdmin
     */
    UmsAdmin getAdminByUsername(String username);

    /**
     * 注册功能
     * @param umsAdminParam     注册UmsAdmin参数
     * @return                  UmsAdmin
     */
    UmsAdmin register(UmsAdmin umsAdminParam);

    /**
     * 登录
     * @param username      用户名
     * @param password      密码
     * @return              Token字符串
     */
    String login(String username, String password);

    /**
     * 获取用户所有权限---角色权限和+-权限
     * @param adminId
     * @return
     */
    List<UmsPermission> getPermissionList(Long adminId);
}

UmsPermission实体类

UserDetailsService实现类内通过调用自定义用户信息Service接口查询出UmsPermissionList,构造UserDetails实现类返回

public class UmsPermission implements Serializable {
    private Long id;

    @ApiModelProperty(value = "父级权限id")
    private Long pid;

    @ApiModelProperty(value = "名称")
    private String name;

    @ApiModelProperty(value = "权限值")
    private String value;

    @ApiModelProperty(value = "图标")
    private String icon;

    @ApiModelProperty(value = "权限类型:0->目录;1->菜单;2->按钮(接口绑定权限)")
    private Integer type;

    @ApiModelProperty(value = "前端资源路径")
    private String uri;

    @ApiModelProperty(value = "启用状态;0->禁用;1->启用")
    private Integer status;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    @ApiModelProperty(value = "排序")
    private Integer sort;

    private static final long serialVersionUID = 1L;
}

UmsAdmin实体类

UserDetailsService实现类内通过调用自定义用户信息Service接口查询出UmsAdmin,构造UserDetails实现类返回

public class UmsAdmin implements Serializable {
    private Long id;

    private String username;

    private String password;

    @ApiModelProperty(value = "头像")
    private String icon;

    @ApiModelProperty(value = "邮箱")
    private String email;

    @ApiModelProperty(value = "昵称")
    private String nickName;

    @ApiModelProperty(value = "备注信息")
    private String note;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    @ApiModelProperty(value = "最后登录时间")
    private Date loginTime;

    @ApiModelProperty(value = "帐号启用状态:0->禁用;1->启用")
    private Integer status;

    private static final long serialVersionUID = 1L;

Jwt过滤器

这里直接在SpringSecurity内注入了,也可以通过其他注解注入配置好的组件

/**
 * Jwt过滤器
 */
@Bean
public JwtAuthentucationTokenFilter jwtAuthentucationTokenFilter(){
    JwtAuthentucationTokenFilter jwtAuthentucationTokenFilter = new JwtAuthentucationTokenFilter();
    return jwtAuthentucationTokenFilter;
}

Jwt过滤器内细节内容

调用重写的UserDetails接口方法,返回用户信息

UserDetails UserDetailsService.loadUserByUsername(String username)

通过Token校验的请求将会设置到SecurityContextHolder

void SecurityContextHolder.getContext().setAuthentication(Authentication authentication);

Filter完整代码

/**
 * Jwt登录授权过滤器
 * 为什么用Filter而不是Inteceptor -- SpringSecurity实现
 */
public class JwtAuthentucationTokenFilter extends OncePerRequestFilter {
    private static final Logger LOGGER = LoggerFactory.getLogger(JwtAuthentucationTokenFilter.class);
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private JwtTokenUtils jwtTokenUtils;

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String authHeader =  httpServletRequest.getHeader(this.tokenHeader);
        if (StringUtils.isNotBlank(authHeader) && authHeader.startsWith(tokenHead)){
            //截取Bearer后
            String authToken = authHeader.substring(this.tokenHead.length());
            String username = jwtTokenUtils.getUsernameFromToken(authToken);
            LOGGER.info("checking username:{}",username);
            if (StringUtils.isNotBlank(username) && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())){
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                //检验是否过期
                if (jwtTokenUtils.validateToken(authToken,userDetails)){
                    //UsernamePasswordAuthenticationToken 管理对应用户
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,
                                                                                                            null,
                                                                                                    userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                    LOGGER.info("authenticated user: {}", username);
                    //设置到SecurityContextHolder
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}
  • @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    

自定义未授权返回

/**
 * 当访问接口没有权限,自定义返回结果
 */
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json");
        httpServletResponse.getWriter().println(
                JSONUtil.parse(CommonResult.forbiden())
        );
        httpServletResponse.getWriter().flush();
    }
}

未登录结果返回

/**
 * 当未登录或者token失效访问接口时,自定义返回结果
 */
@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json");
        httpServletResponse.getWriter().println(
                JSONUtil.parse(CommonResult.unauthorized())
        );
        httpServletResponse.getWriter().flush();
    }
}

AuthenticationManager

在这里直接在SpringSecurity类内注入

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}	

AuthenticationManager提供了方法

//实现类ProviderManager
Authentication authenticate(Authentication authentication) throws AuthenticationException;

AuthenticationManager.authenticate方法内,会遍历寻找可以处理的AuthenticationProvider,调用

//DaoAuthenticationProvider继承自AbstractUserDetailsAuthenticationProvider
//AbstractUserDetailsAuthenticationProvider实现了AuthenticationProvider接口
Authentication AuthenticationProvider.authenticate(Authentication authentication) throws AuthenticationException;

AuthenticationProvider.authenticate方法内,调用抽象方法retrieveUser

DaoAuthenticationProvider.retrieveUser方法内,调用UserDetailsService根据username进行查询返回UserDetails,调用方法检查UserDetails内是否可用,并返回最终结果

Controller接口

品牌管理接口

PmsBrandController

定义接口权限注解

@PreAuthorize("hasAuthority('pms:brand:update')")

注意:UserDetails获得的用户用户权限列表,应该包含这个权限字段(pms:brand:update),也就是要与数据库存储的权限信息格式对应

Collection<? extends GrantedAuthority> UserDetails.getAuthorities();
/**
 * 品牌管理Controller
 */
@Api(tags = "PmsBrandController")
@RestController
@RequestMapping("/brand")
public class PmsBrandController {

    @Autowired
    PmsBrandService pmsBrandService;

    //生成日志Logger对象
    private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

    @ApiOperation("获取所有品牌类别")
    @PreAuthorize("hasAuthority('pms:brand:read')")
    @GetMapping("/listAll")
    public CommonResult listAll(){
        return CommonResult.success(pmsBrandService.listAllBrand());
    }

    @ApiOperation("添加品牌")
    @PreAuthorize("hasAuthority('pms:brand:create')")
    @PostMapping("/create")
    public CommonResult createBrand(PmsBrand pmsBrand){
        boolean flag = pmsBrandService.createBrand(pmsBrand);
        if (flag){
            return CommonResult.success();
        }else {
            LOGGER.info("createBrand操作失败");
            return CommonResult.failed();
        }
    }

    @ApiOperation("删除指定Id品牌信息")
    @PreAuthorize("hasAuthority('pms:brand:delete')")
    @PostMapping("/delete")
    public CommonResult deleteBrand(long id){
        boolean flag = pmsBrandService.deleteBrand(id);
        if (flag){
            return CommonResult.success();
        }else {
            LOGGER.info("deleteBrand操作失败");
            return CommonResult.failed();
        }
    }

    @ApiOperation("更新指定Id品牌信息")
    @PreAuthorize("hasAuthority('pms:brand:update')")
    @PostMapping("/update/{id}")
    public CommonResult updateBrand(@PathVariable("id") long id, PmsBrand pmsBrand){
        boolean flag = pmsBrandService.updateBrand(id, pmsBrand);
        if (flag){
            return CommonResult.success();
        }else {
            LOGGER.info("updateBrand操作失败");
            return CommonResult.failed();
        }
    }

    @ApiOperation("分页查询品牌信息")
    @PreAuthorize("hasAuthority('pms:brand:read')")
    @GetMapping("/list")
    public CommonResult pageBrand(@RequestParam(value = "pageNum",defaultValue = "1") int pageNum,
                                  @RequestParam(value = "paegSize",defaultValue = "3") int pageSize){
        return CommonResult.success(pmsBrandService.listPmsBrand(pageNum,pageSize));
    }

    @ApiOperation("查询指定Id品牌信息")
    @PreAuthorize("hasAuthority('pms:brand:read')")
    @GetMapping("/{id}")
    public CommonResult getBrandById(@PathVariable("id") long id){
        return CommonResult.success(pmsBrandService.getPmsBrand(id));
    }
}

用户功能接口

UmsAdminController

@RestController
@RequestMapping("/admin")
@Api(tags = "UmsAdminController", description = "后台用户管理")
public class UmsAdminController {

    @Autowired
    private UmsAdminService umsAdminService;
    @Value("jwt.tokenHead")
    private String tokenHead;
    @Value("jwt.tokenHeader")
    private String tokenHeader;

    @ApiOperation(value = "用户注册")
    @PostMapping("/register")
    public CommonResult register(UmsAdmin umsAdmin){
        UmsAdmin umsAdminResult = umsAdminService.register(umsAdmin);
        if (Objects.isNull(umsAdminResult)){
            return CommonResult.failed("注册失败");
        }
        return CommonResult.success(umsAdminResult);
    }

    @ApiOperation("登陆后返回Token")
    @PostMapping("/login")
    public CommonResult login(String username, String password){
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
            return CommonResult.validateFailed();
        }
        String token = umsAdminService.login(username, password);
        if (StringUtils.isBlank(token)){
            return CommonResult.validateFailed("用户名或密码错误");
        }
        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put("token",token);
        tokenMap.put("tokenHead",tokenHead);
        return CommonResult.success(tokenMap);
    }

    @ApiOperation("获取权限列表")
    @GetMapping("/permission/{adminId}")
    public CommonResult getPermissionList(@PathVariable("adminId") long adminId){
        List<UmsPermission> permissionList = umsAdminService.getPermissionList(adminId);
        return CommonResult.success(permissionList);
    }
}