学习日志day72(2021-11-13)(1、postman接口测试工具 2、认证接口 3、shiro权限框架)

学习内容:学习接口认证和shiro(Day72)

1、postman接口测试工具
2、认证接口
3、shiro权限框架


1、postman接口测试工具

(1)Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。postman适用于不同的操作系统,Postman Mac、Windows X32、Windows X64、Linux系统,还支持postman 浏览器扩展程序、postman chrome应用程序等。下载地址:https://www.postman.com/downloads/

(2)collection在postman里面相当于一个文件夹,可以把同一个项目的请求放在一个Collection里方便管理和分享,Collection里面也可以再建文件夹。这里我们做一个普通接口的简单的示例

学习日志day72(2021-11-13)(1、postman接口测试工具 2、认证接口 3、shiro权限框架)_第1张图片

2、认证接口

(1)basic auth认证
使用postman创建一个接口调用,请求URL:https://postman-echo.com/basic-auth
用户名:postman
密码:password
授权协议:Basic auth
学习日志day72(2021-11-13)(1、postman接口测试工具 2、认证接口 3、shiro权限框架)_第2张图片

Authorization:用于需要认证的接口。
Basic Auth:最基本的一种认证类型,还有OAuth 1.0/2.0、Digest Auth等认证类型。
Username/Password:这是针对Basic Auth类型的认证的用户名/密码,并非我们认为的系统登录的用户名密码。

(2)在项目中加入添加依赖也可以实现basic auth认证

<dependency>
  <groupId>org.springframework.bootgroupId>
  <artifactId>spring-boot-starter-securityartifactId>
dependency>

然后访问页面时会跳转到认证页面,用户名默认是user,密码自动生成显示在控制台。

(3)bearer token认证
为了验证使用者的身份,需要客户端向服务器端提供一个可靠的验证信息,称为Token,这个token通常由Json数据格式组成,通过hash散列算法生成一个字符串,所以称为Json Web Token(Json表示令牌的原始值是一个Json格式的数据,web表示是在互联网传播的,token表示令牌,简称JWT)。

测试带token的接口,首先要进行登录,登录成功会有个token信息,向api接口发送请求的时候必须在header中放入这个token,故需要做2次请求。

(4)Springboot 使用JWT 生成token
JWT意思是Json web token,通过POST参数或者在HTTP header发送,然后进行验证,验证通过之后,就能返回响应的资源给浏览器。通常和权限框架(如shiro)搭配使用。

1.引入依赖

<dependency>
    <groupId>com.auth0groupId>
    <artifactId>java-jwtartifactId>
    <version>3.4.0version>
dependency>

2.在annotation包下自定义注解
用来跳过验证的@PassToken

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface PassToken {
    boolean required() default true;
}

需要登录才能进行操作的注解UserLoginToken

@Target({ElementType.METHOD, ElementType.TYPE}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface UserLoginToken {
    boolean required() default true;
}

3.封装生成Token的工具类

public class JWTUtils {
    public static String getToken(String userCode, String userPassword) {
        return JWT.create()
                .withAudience(userCode)
                .sign(Algorithm.HMAC256(userPassword));
    }
}

4.新增一个拦截器AuthenticationInterceptor

public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
    @Resource
    private UserMapper userMapper;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 user id
                String userCode;
                try {
                    userCode = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("token验证失败");
                }
                User user = userMapper.selectUserByUserCode(userCode);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getUserPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("token验证失败");
                }
                return true;
            }
        }
        return true;
    }
}

5.配置拦截器

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor(){
        return new AuthenticationInterceptor();
    }
}

6.验证
不加注解的话默认不验证,登录接口一般是不验证的。在/hello中加上了登录注解,说明该接口必须登录获取token后,在请求头中加上token并通过验证才可以访问

@RestController
public class UserController {
    @Autowired
    private UserMapper userMapper;
    @PassToken
    @RequestMapping("/login")
    public Object login(@RequestParam("userCode")String userCode,
                        @RequestParam("userPassword")String userPassword){
        User user = userMapper.selectUserByUserCode(userCode);
        Map map = new HashMap();
        if(user.getUserPassword().equals(userPassword)){
            map.put("status",1);
            map.put("msg","success");
            map.put("token", JWTUtils.getToken(userCode,userPassword));
        }else{
            map.put("status",0);
            map.put("msg","error");
        }
        return map;
    }

    @UserLoginToken
    @RequestMapping("/hello")
    public Object hello(){
        Map map = new HashMap();
        map.put("name","郭靖");
        map.put("birthday",new Date());
        List<User> userList = userMapper.getUserList();
        map.put("userlist",userList);
        return map;
    }

}

3、shiro权限框架

(1)shiro是一个权限框架,用来进行javaSE和javaEE中的权限操作。原理是通过Filter来对资源进行拦截。比Spring security更简单易用,更主流。

添加依赖

<dependency>
    <groupId>org.apache.shirogroupId>
    <artifactId>shiro-springartifactId>
    <version>1.7.1version>
dependency>

springboot中集成shiro需要两个类,shiroConfig类和CustomRealm类。
ShiroConfig类:对shiro的一些配置,相对于之前的xml配置。包括:过滤的文件和权限,密码加密的算法,其用注解等相关功能。

@Configuration
public class ShiroConfig {

    //开启AOP
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    //将自己的验证方式加入容器
    @Bean
    public CustomRealm myShiroRealm() {
        return new CustomRealm();
    }

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        //登出
        map.put("/logout", "logout");
        //对所有用户认证
        map.put("/**", "authc");
        //登录
        shiroFilterFactoryBean.setLoginUrl("/login");
        //首页
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //错误页面,认证不通过跳转
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    //注入权限管理
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

CustomRealm类:自定义的CustomRealm继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限相关)、doGetAuthenticationInfo(身份认证)这两个方法。

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    /**
     * @MethodName doGetAuthorizationInfo
     * @Description 权限配置类
     * @Param [principalCollection]
     * @Return AuthorizationInfo
     * @Author hisoft
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获取登录用户名
        String name = (String) principalCollection.getPrimaryPrincipal();
        //查询用户名称
        User user = loginService.getUserByName(name);
        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        for (Role role : user.getRoles()) {
            //添加角色
            simpleAuthorizationInfo.addRole(role.getRoleName());
            //添加权限
            for (Permissions permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
            }
        }
        return simpleAuthorizationInfo;
    }

    /**
     * @MethodName doGetAuthenticationInfo
     * @Description 认证配置类
     * @Param [authenticationToken]
     * @Return AuthenticationInfo
     * @Author hisoft
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
            return null;
        }
        //获取用户信息
        String name = authenticationToken.getPrincipal().toString();
        User user = loginService.getUserByName(name);
        if (user == null) {
            //这里返回后会报出对应异常
            return null;
        } else {
            //这里验证authenticationToken和simpleAuthenticationInfo的信息
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword(), getName());
            return simpleAuthenticationInfo;
        }
    }
}

创建三个实体类,分别是用户、角色、权限

@Data
@AllArgsConstructor
public class User {
    private String id;
    private String userName;
    private String password;
    /**
     * 用户对应的角色集合
     */
    private Set<Role> roles;
}
@Data
@AllArgsConstructor
public class Role {

    private String id;
    private String roleName;
    /**
     * 角色对应权限集合
     */
    private Set<Permissions> permissions;

}
@Data
@AllArgsConstructor
public class Permissions {
    private String id;
    private String permissionsName;
}

service层

@Service
public class LoginServiceImpl implements LoginService {

    @Override
    public User getUserByName(String getMapByName) {
        return getMapByName(getMapByName);
    }

    /**
     * 模拟数据库查询
     *
     * @param userName 用户名
     * @return User
     */
    private User getMapByName(String userName) {
        Permissions permissions1 = new Permissions("1", "query");
        Permissions permissions2 = new Permissions("2", "add");
        Set<Permissions> permissionsSet = new HashSet<>();
        permissionsSet.add(permissions1);
        permissionsSet.add(permissions2);
        Role role = new Role("1", "admin", permissionsSet);
        Set<Role> roleSet = new HashSet<>();
        roleSet.add(role);
        User user = new User("1", "hisoft", "123456", roleSet);
        Map<String, User> map = new HashMap<>();
        map.put(user.getUserName(), user);

        Set<Permissions> permissionsSet1 = new HashSet<>();
        permissionsSet1.add(permissions1);
        Role role1 = new Role("2", "user", permissionsSet1);
        Set<Role> roleSet1 = new HashSet<>();
        roleSet1.add(role1);
        User user1 = new User("2", "zhangsan", "123456", roleSet1);
        map.put(user1.getUserName(), user1);
        return map.get(userName);
    }
}

controller层

@RestController
@Slf4j
public class LoginController {

    @GetMapping("/login")
    public String login(User user) {
        if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
            return "请输入用户名和密码!";
        }
        //用户认证信息
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
                user.getUserName(),
                user.getPassword()
        );
        try {
            //进行验证,这里可以捕获异常,然后返回对应信息
            subject.login(usernamePasswordToken);
//            subject.checkRole("admin");
//            subject.checkPermissions("query", "add");
        } catch (UnknownAccountException e) {
            log.error("用户名不存在!", e);
            return "用户名不存在!";
        } catch (AuthenticationException e) {
            log.error("账号或密码错误!", e);
            return "账号或密码错误!";
        } catch (AuthorizationException e) {
            log.error("没有权限!", e);
            return "没有权限";
        }
        return "login success";
    }

    @RequiresRoles("admin")
    @GetMapping("/admin")
    public String admin() {
        return "admin success!";
    }

    @RequiresPermissions("query")
    @GetMapping("/index")
    public String index() {
        return "index success!";
    }

    @RequiresPermissions("add")
    @GetMapping("/add")
    public String add() {
        return "add success!";
    }
}

你可能感兴趣的:(学习日志,postman,shiro)