zoukankan      html  css  js  c++  java
  • SpringBoot+Shiro (一)

    从网上搜索SpringBoot+Shiro相关文章,大部分都需要DB和Ecache的支持。这里提供一个最简单的Spring+Shiro的配置。

    前言:

    1. 由于SpringBoot官方已经不再建议使用jsp,并且前后端分离及服务化的大趋势也越来越强烈,所以本文旨在搭建一个Restfull的web服务。

    2. Rest接口的授权基于Shiro注解的方式实现,这样更灵活更容易掌控。

    pom依赖:

    这是最小化的依赖项,缺一不可。

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
    </dependency>

    创建一个Realm

    这里面用户认证和授权没有通过连接DB的方式实现,用户信息(用户名、密码)以及用户权限直接硬编码到了代码里。从代码可看出这个例子只支持两个用户:admin/admin、guest/guest,且admin用户拥有角色admin和权限permission1、permission2,guest用户拥有角色guest和权限permission3、permission4.

    public class PropertiesRealm extends AuthorizingRealm {
        
        // 用户认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            if (authenticationToken instanceof UsernamePasswordToken) {
                UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
                String username = token.getUsername();
                String password = new String(token.getPassword());
                if ((username.equals("admin") && password.equals("admin")) ||
                        (username.equals("guest") && password.equals("guest"))) {
                    return new SimpleAuthenticationInfo(username, password, getName());
                } else {
                    throw new AuthenticationException("用户名或密码错误");
                }
            } else if (authenticationToken instanceof RememberMeAuthenticationToken) {
                RememberMeAuthenticationToken token = (RememberMeAuthenticationToken) authenticationToken;
                // TODO: 2018/10/24
                return null;
            } else if (authenticationToken instanceof HostAuthenticationToken) {
                HostAuthenticationToken token = (HostAuthenticationToken) authenticationToken;
                // TODO: 2018/10/24
                return null;
            } else {
                throw new AuthenticationException("未知的AuthenticationToken类型");
            }
        }
    
        // 用户授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            String username = (String) principalCollection.getPrimaryPrincipal();
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            if (username.equals("admin")) {
                simpleAuthorizationInfo.addRole("admin");
                simpleAuthorizationInfo.addStringPermission("permission1");
                simpleAuthorizationInfo.addStringPermission("permission2");
            } else if (username.equals("guest")) {
                simpleAuthorizationInfo.addRole("guest");
                simpleAuthorizationInfo.addStringPermission("permission3");
                simpleAuthorizationInfo.addStringPermission("permission4");
            }
            return simpleAuthorizationInfo;
        }
    }

    创建SpringBootApplication

    下面这段代码中,需要注意的就是ShiroFilterFactoryBean和SimpleMappingExceptionResolver这两个bean:

    1. SimpleMappingExceptionResolver:Shiro通过注解的方式校验用户认证和授权时,如果用户未认证或权限不足,Shiro不会进行页面跳转,而是直接抛出异常。所以我们需要定义SimpleMappingExceptionResolver来处理这两个异常,以保证我们的rest接口在用户未认证或权限不足的时候返回正确的json数据。

    2. ShiroFilterFactoryBean:这个bean不需要setSuccessUrl()、setLoginUrl()和setUnauthorizedUrl()。因为Shiro通过注解的方式校验用户认证和授权时,如果用户未认证或权限不足,Shiro不会进行页面跳转,而是直接抛出异常,所以setLoginUrl()和setUnauthorizedUrl()就不需要了。由于我们是做Rest接口服务,那么用户认证过程中也是调用Rest API校验用户身份,校验通过后由前端页面路由至登录成功页面,所以setSuccessUrl()也不需要了。

    @SpringBootApplication
    public class ShiroApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ShiroApplication.class);
        }
    
        @Bean
        public PropertiesRealm propertiesRealm() {
            return new PropertiesRealm();
        }
    
        @Bean
        public SecurityManager securityManager(PropertiesRealm propertiesRealm) {
            return new DefaultWebSecurityManager(propertiesRealm);
        }
    
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
        @Bean
        public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
            Properties properties = new Properties();
            properties.put(UnauthenticatedException.class.getName(), "/unauthenticated");
            properties.put(UnauthorizedException.class.getName(), "/unauthorized");
    
            SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
            simpleMappingExceptionResolver.setExceptionMappings(properties);
            return simpleMappingExceptionResolver;
        }
    
    }

    创建统一的Restfull返回结果包装:

    public class Result {
    
        private int code = 200;
        private String message = "success";
        private Object data;
    
        public Result() {
        }
    
        public Result(int code, String message) {
            this.code = code;
            this.message = message;
        }
    
        public Result(Object data) {
            this.data = data;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public Object getData() {
            return data;
        }
    }

    创建Controler

    方法说明:

    • unauthenticated:根据SimpleMappingExceptionResolver的配置,如果Shiro检测方法请求需要用户登录,则会重定向到/unauthenticated,返回401“需要登录”,以便前端根据状态码做相应的路由跳转。
    • unauthorized:根据SimpleMappingExceptionResolver的配置,如果Shiro检测到用户权限不足,则会重定向到/unauthorized,返回401“未授权”,以便前端根据状态码做相应的路由跳转。
    • login:用户登录
    • logout:用户登出
    • getData1、getData2:用来做测试用。getData1需要用户认证,用户完成认证后即可访问;getData2需要用户拥有“admin”角色才能访问。
    @RestController
    public class LoginController {
    
    
        @GetMapping("unauthenticated")
        public Result unauthenticated() {
            return new Result(401, "需要登录");
        }
    
        @GetMapping("unauthorized")
        public Result unauthorized() {
            return new Result(401, "未授权");
        }
    
        @PostMapping("login")
        public Result login(String username, String password) {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            subject.login(token);
            return new Result();
        }
    
        @PostMapping("logout")
        public Result logout() {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return new Result();
        }
    
    
        @GetMapping("getData1")
        @RequiresUser
        public Result getData1() {
            return new Result("this is data1");
        }
    
        @GetMapping("getData2")
        @RequiresRoles("admin")
        public Result getData2() {
            return new Result("this is data2");
        }
    }
  • 相关阅读:
    C# 类 根据属性名称取属性值
    WebService WCF 契约的解释
    NHibenate xml 数据库配置 说明
    使Spring.NET的IOC容器支持动态加载的程序集
    知识点滴:持久层,DAO,API,DAL,BLL,DLL,csproj,sln
    spring.net nhibernate 分布布式事务(下)
    简明教程 C# Webservice实例
    C# 中 以 &# 打头的 编码 是: html 编码解码地址 转换
    spring.net 结合简单三层实例
    关于行号输出的简单命令
  • 原文地址:https://www.cnblogs.com/LOVE0612/p/9851017.html
Copyright © 2011-2022 走看看