SpringBoot+Security+Jwt简单应用-V1.0

一,Security

        认证:验证当前用户是否为本系统用户,并且要确认是哪一个用户。

        授权:经过认证后判断当前用户是否有权限进行某个操作。

二,添加 Security 依赖

<!--security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>2.5.4</version>
</dependency>

三,配置 SecurityConfig 配置类

package com.guo.config.security;

import com.guo.config.security.exception.AccessDeniedHandlerImpl;
import com.guo.config.security.exception.AuthenticationEntryPointImpl;
import com.guo.config.security.filter.JwtAuthenticationTokenFilter;
import com.guo.config.security.handler.LoginFailHandler;
import com.guo.config.security.handler.MyLoginSuccessHandler;
import com.guo.config.security.handler.MyLogoutSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
//开启 注解使用功能
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyLoginSuccessHandler loginSuccessHandler;
    @Autowired
    private LoginFailHandler failHandler;
    @Autowired
    private MyLogoutSuccessHandler logoutSuccessHandler;
    @Autowired
    private JwtAuthenticationTokenFilter tokenFilter;
    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;
    @Autowired
    private AccessDeniedHandlerImpl accessDeniedHandler;


    /**
     * 创建 BCryptPasswordEncoder 注入容器中
     * @return
     */
    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     *  获取 认证的 bean  注入spring容器
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     *   一般用这个 http
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // 前后端分离 一般关闭跨域token  一般用不到
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                //对于登录接口允许 匿名访问
                // anonymous 表示匿名访问(表示未登录状态即可访问)
                // permitAll 表示 登录状态或未登录状态都可以访问      前后端不分离需要配置好多这种路径
                .antMatchers("/user/login").anonymous()
                // 放行申请授权路径     /test/hello测试过滤拦截
                .antMatchers("/oauth/**","/test/hello").permitAll()
                //下边的是 基于配置类的权限配置
//                .antMatchers("/test/hello").hasAuthority("admin")
                //除上边的所有请求都需要经过鉴权认证
                .anyRequest().authenticated();

        // 之前定义的过滤器只是放在 spring 容器当中 security 需要使用就需要进行如下配置
        //参数一:表示自定义的过滤器   参数二 : 字节码对象  表示需要把自定义过滤器放在哪一个过滤器之前
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);

        //配置异常处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);

        //允许跨域
//        http.cors();

        //开启表单
//        http.formLogin().permitAll();

    }

//    /**
//     *  授权码模式测试
//     * @param http
//     * @throws Exception
//     */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.cors().disable()//禁用跨域
//                .authorizeRequests()//配置权限
//                .antMatchers("/oauth/**").permitAll()//oauth接口全部允许访问
//                .anyRequest().authenticated()//其他接口需要认证
//                .and()
//                .formLogin().permitAll();//放行登录接口(表单)
//    }


//    /**
//     *   配置登陆成功处理器   配置登录失败处理器  配置登出 注销成功处理器
//     *   进行别的可以将 此注释   否则会产生那个冲突
//     * @param http
//     * @throws Exception
//     */
//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        //调用父类的方法
        super.configure(http);
//        http.formLogin()
//                //配置登陆成功处理器
//                .successHandler(loginSuccessHandler)
//                //配置登录失败处理器
//                .failureHandler(failHandler)
//                .and()
//                //配置登出 注销成功处理器
//                .logout()
//                .logoutSuccessHandler(logoutSuccessHandler)
//                .and()
//                //配置认证  下述表示所有接口都需要认证
//                .authorizeRequests()
//                //授权码测试  放行路径
                .antMatchers("/oauth/**").anonymous()
//                .anyRequest()
//                .authenticated();
//
//    }
}

四,添加 JwtToken过滤器

package com.guo.config.security.filter;

import com.guo.config.security.entity.LoginUser;
import com.guo.config.security.jwt.SanGengJwtUtils;
import com.guo.exception.MsgException;
import com.guo.exception.MyException;
import com.guo.utils.StringUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// TODO: 2022/8/25 项目中两个过滤器怎样实现顺序    一个Filter一个OncePerRequestFilter
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        log.info("OncePerRequestFilter 执行");
//        filterChain.doFilter(httpServletRequest,httpServletResponse);

        String requestURI = httpServletRequest.getRequestURI();

        //获取 token
        //此处表示 未携带 token 直接走人
        String token = httpServletRequest.getHeader("token");
        if (StringUtils.isEmpty(token)) {
            //放行  方便后续过滤器的执行
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            //return 的作用是  过滤器链会执行两次 如果不return的话 响应过滤器链时会再次执行 解析 token
            return;
        }
        //解析 token
        Claims claims = null;
        String userId;
        try {
            claims = SanGengJwtUtils.parseJWt(token);
            userId = claims.getSubject();

        } catch (Exception e) {
            e.printStackTrace();
            throw new MyException(MsgException.TOKEN_PARSE_EXCEPTION);
        }
        //从 redis 中获取用户信息
        String redisKey = "token:" + userId;
        LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get(redisKey);
        if (loginUser == null) {
            //可能表示用户登录过期 啥的     此处表示有 token 但是 未查到用户信息  redis里边没有
            // TODO: 2022/8/25   token这里逻辑有点迷
            // TODO: 2022/8/25 还有为什么这里抛出自定义异常  自己的异常没有转接到异常处理
            throw new MyException(MsgException.TOKEN_PARSE_EXCEPTIONS);
        }
        //存入 SecurityContextHolder  中  之后就有权限访问其他接口
        //三个参数 表示用户已经处于认证状态  之后访问其他接口就不需要在去认证
        // 参数一 用户信息   参数二 :不知道 null 参数三 :用户所具有的权限  现在没有可以先设置为 null
        // TODO: 2022/8/25 随后获取权限信息封装传入  authorites
//        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        // TODO: 2022/8/26 权限信息已写入
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getList());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行  方便后续过滤器的执行
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

五:UserDetails 的实现类 LoginUser

package com.guo.config.security.entity;

import com.alibaba.fastjson.annotation.JSONField;
import com.guo.entity.Admin;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;


@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {

    private Admin admin;
    
    //查询封装的权限集合
    private List<String> authorityList;
    
    // 认证需要的权限集合
    // 注解表示 此属性不会被序列化
    //在登录的时候会把 LoginUser  存入redis 当中  所以添加此注解
//    @JSONField(serialize = false)
    private List<SimpleGrantedAuthority> list;

    public LoginUser(Admin admin, List<String> authorityList) {
        this.admin = admin;
        this.authorityList = authorityList;
    }

    //    /**
//     *
//     *  老版本
//     * 用于返回权限信息
//     * @return
//     */
//    @Override
//    public Collection<? extends GrantedAuthority> getAuthorities() {
//        //security查询权限信息 调用此方法  需要将自己的权限信息写入
//        //GrantedAuthority  实现类
//        //将 authorityList 封装的string 权限信息 封装成SimpleGrantedAuthority 对象
        List<GrantedAuthority> list = new ArrayList<>();
        for (String authority : authorityList) {
            SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authority);
            list.add(simpleGrantedAuthority);
        }
//
//        //java 8新特性
//        List<SimpleGrantedAuthority> list = authorityList.stream()
//                .map(SimpleGrantedAuthority::new)
//                .collect(Collectors.toList());
//
//        return list;
//    }

    /**
     * 优化
     * 用于返回权限信息
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //security查询权限信息 调用此方法  需要将自己的权限信息写入
        //GrantedAuthority  实现类
        //将 authorityList 封装的string 权限信息 封装成SimpleGrantedAuthority 对象
        if (list != null) {
            return list;
        }
        //java 8新特性
        list = authorityList.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());
        return list;
    }
    
    /**
     * 获取用户认证密码
     * @return
     */
    @Override
    public String getPassword() {
        return admin.getPassword();
    }

    /**
     * 获取用户认证用户名
     * @return
     */
    @Override
    public String getUsername() {
        return admin.getName();
    }

    /**
     * 是否没过期
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 用户是否可用
     * @return
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}

六:UserDetailsService 的实现类 UserDetailsServiceImpl

package com.guo.config.security.service.impl;

import com.guo.config.security.entity.LoginUser;
import com.guo.entity.Admin;
import com.guo.exception.MsgException;
import com.guo.exception.MyException;
import com.guo.service.AdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private AdminService adminService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
        //查询用户信息
        Admin admin = adminService.selectByName(username);

        //自己的理解  此处暂时没有拿到用户密码  但是可以从数据库中拿到用户信息
        //可以在此处校验   用户名不存在,用户被冻结 抛出此等异常

        if (admin == null) {
            System.out.println("查询不到用户");
            throw new MyException(MsgException.USER_NAME_PASSWORD_EXCEPTION);
//            throw new UsernameNotFoundException("用户名密码错误");
        }
        // TODO: 2022/8/24 查询权限信息   用户里边放入权限信息   返回给登录接口
        //先将权限信息写死 后续处理
        List<String> authorityList = new ArrayList<>(Arrays.asList("test    ","admin"));
        //此处都是数据库账号密码都正确的情况下
        //封装成 UserDetail对象返回   用于authenticationManager.authenticate  获取
        return new LoginUser(admin,authorityList);
    }
}

七,LoginService 接口

package com.guo.config.security.service;

import com.guo.entity.Admin;
import com.guo.utils.Result;

public interface LoginService  {

    Result login(Admin admin);

    Result logout();
}

八:LoginService 接口实现类 LoginServiceImpl

package com.guo.config.security.service.impl;

import com.guo.config.security.jwt.SanGengJwtUtils;
import com.guo.config.security.entity.LoginUser;
import com.guo.config.security.service.LoginService;
import com.guo.entity.Admin;
import com.guo.exception.MsgException;
import com.guo.exception.MyException;
import com.guo.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl  implements LoginService {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    public Result login(Admin admin) {
        // authenticationManager.authenticate() 进行用户认证
        // 将 用户名和密码 封装成  AuthenticationManager 接口类型进行认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(admin.getName(), admin.getPassword());

        // authenticationManager.authenticate() 认证时会调用 UserDetailsService  的loadUserByUsername  方法
        //进行认证   也就是自己写的UserDetailsService 实现类逻辑
        Authentication authenticate = null;
        try {
            //authenticate 里边有各种属性
            // 此认证方法会抛出各种异常  此方法会校验用户和密码
            authenticate = authenticationManager.authenticate(authenticationToken);

            //此处的异常捕捉可以放在  实现啦UserDetailsService 的实现类中
            //BadCredentialsException  可以查询到用户,但是密码错误
            //InternalAuthenticationServiceException    查询不到用户,没有该用户
        } catch (InternalAuthenticationServiceException e) {
            throw new MyException(MsgException.USER_NAME_PASSWORD_EXCEPTION);
        }
        //如果认证没通过 给出对应提示

        //如果认证通过,使用 userId生成 jwt,jwt存入 Result 进行返回
        //userId  从 authenticate 中获取
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        Integer id = loginUser.getAdmin().getId();
        String jwt = SanGengJwtUtils.createJWT(id.toString());
        Map<String, Object> map = new HashMap<>();
        map.put("token", jwt);

        //把完整的用户信息  存入redis  userId作为key    60秒过期
        redisTemplate.opsForValue().set("token:" + id, loginUser,2, TimeUnit.MINUTES);

        return Result.buildSuccess(map , 666L);
    }

    @Override
    public Result logout() {
        //获取 SecurityContextHolder 中用户的 id
        // 一个请求过来 会先通过 jet 认证过滤器  因为认证过滤器中已经设置啦 Authentication  所获取到的是同一个
        // Authentication   可以强制转换为 UsernamePasswordAuthenticationToken  用于获取认证信息
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        Integer id = loginUser.getAdmin().getId();
        //删除 Redis 中的 token 值
        String redisKey = "token:" + id;
        redisTemplate.delete(redisKey);
        return Result.buildSuccess("注销成功", 666L);
    }
}

九:自定义 认证?授权?失败异常处理

        WebUtils工具类:

package com.guo.config.security.utils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class WebUtils {

    public static String renderString(HttpServletResponse response, String stringValue) {
        try {
            response.setStatus(200);
            response.setContentType("application/json");
            //设置响应编码
            response.setCharacterEncoding("utf-8");
            response.getWriter().println(stringValue);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //可以不需要返回值
        return null;
    }
}

        授权异常处理:

package com.guo.config.security.exception;

import com.guo.config.security.utils.WebUtils;
import com.guo.utils.JsonUtils;
import com.guo.utils.Result;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * security 自己的全局异常处理
 *  鉴权异常处理
 */
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        Result result = new Result(HttpStatus.METHOD_NOT_ALLOWED.toString(), "用户鉴权失败-security的异常处理", null, 1L);
        String json = JsonUtils.getJson(result);
        WebUtils.renderString(httpServletResponse,json);
    }
}

        认证异常处理:

package com.guo.config.security.exception;

import com.guo.config.security.utils.WebUtils;
import com.guo.utils.JsonUtils;
import com.guo.utils.Result;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * security 自己的全局异常处理
 *  认证异常处理
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
    
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //HttpStatus 里边封装啦各种状态码的信息
        Result result = new Result(HttpStatus.UNAUTHORIZED.toString(), "用户认证失败-security的异常处理", null, 1L);
        //自己写的工具类  对象转字符串
        String json = JsonUtils.getJson(result);
        WebUtils.renderString(httpServletResponse, json);
    }
}

十:自定义登录失败,登录成功,登出成功处理

        登录失败处理:

package com.guo.config.security.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component            //可以实现接口AuthenticationFailureHandler  也可以继承此接口的实现类
public class LoginFailHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        log.info("认证失败------------登录失败");
    }
}

        登录成功处理:

package com.guo.config.security.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component                    //可以实现接口 AuthenticationSuccessHandler 也可以继承接口的实现类
public class MyLoginSuccessHandler implements AuthenticationSuccessHandler {
    //Authentication   用户认证的对象

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        log.info("---------------认证成功");
    }
}

        登出成功处理:

package com.guo.config.security.handler;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class MyLogoutSuccessHandler  implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        log.info("-------------登出成功---------------");
    }
}

十一:自定义权限校验规则

package com.guo.config.security;

import com.guo.config.security.entity.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import java.util.List;

@Component("my")
public class MyAuthority {

    public boolean hasAuthority(String authority) {
        //获取当前用户的权限
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //loginUser 里边直接获取权限列表  可以少一步
        List<String> authorityList = loginUser.getAuthorityList();
        //含有权限则返回true 否则返回false
        return authorityList.contains(authority);
    }
}

十二:Controller 层

package com.guo.config.security.controller;

import com.guo.config.security.service.LoginService;
import com.guo.entity.Admin;
import com.guo.utils.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/user")
public class MyLoginController {

    @Autowired
    private LoginService loginService;

    @PostMapping("/login")
    @ResponseBody
    public Result login(@RequestBody Admin admin) {
        // TODO: 2022/8/25 测试一下不适用RequestBody注解 可以获取数据不能

        //登录
        Result login = loginService.login(admin);
        return login;
    }

    /**
     *  退出登录
     * */
    @GetMapping("/logout")
    @ResponseBody
    public Result logout() {
        return loginService.logout();
    }

    @GetMapping("/hello")
    //自定义的 权限校验规则
//    @PreAuthorize("@my.hasAuthority('test')")
    // 此注解需要一个 String类型的参数
    // 此参数为一个方法  其实扫描后就是一个方法的调用   方法参数为String类型 使用单引号
    // 固定格式
    @PreAuthorize("hasAuthority('test')")
    @ResponseBody
    public String getHello() {
        return "test hello";
    }

}

十三:里边都有注释

controller->service.login->~~authentication->loadByUsername->tokenFilter

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值