shiro(二)

Springboot整合shiro

了解了shiro的基本概念后,现在就基于springboot整合shiro

pom
<dependencies>
        <!-- spring -->
        <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>

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.6.0</version>
        </dependency>
    </dependencies>
model
/**
 * 用户实体类
 */
public class UserDO {

    private Long userId;
    private String username;
    private String password;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
/**
 * 资源实体类
 */
public class ResourceDO {

    String resId;
    String resName;

    public String getResId() {
        return resId;
    }

    public void setResId(String resId) {
        this.resId = resId;
    }

    public String getResName() {
        return resName;
    }

    public void setResName(String resName) {
        this.resName = resName;
    }
}
service层
@Service
public class BaseService {

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    public UserDO selectByUsername(String username) {
        // 模拟DAO查询结果
        UserDO user = new UserDO();
        user.setUserId(1L);
        user.setUsername(username);
        user.setPassword("123456");
        return user;
    }

    /**
     * 根据用户名查询用户权限列表
     * @param username
     * @return
     */
    public List<ResourceDO> selectResourceByUsername(String username) {
        // 模拟DAO查询结果
        List<ResourceDO> resourceDOList = new LinkedList<>();
        ResourceDO res1 = new ResourceDO();
        res1.setResId("CLIENT:ADD");
        res1.setResName("客户新增");
        resourceDOList.add(res1);
        ResourceDO res2 = new ResourceDO();
        res2.setResId("CLIENT:UPDATE");
        res2.setResName("客户修改");
        resourceDOList.add(res2);
        ResourceDO res3 = new ResourceDO();
        res3.setResId("CLIENT:DEL");
        res3.setResName("客户删除");
        resourceDOList.add(res3);
        ResourceDO res4 = new ResourceDO();
        res4.setResId("CLIENT:QUERY");
        res4.setResName("客户查询");
        resourceDOList.add(res1);
        resourceDOList.add(res4);
        return resourceDOList;
    }
}
controller层
@RestController
@RequestMapping("admin")
public class BaseController {

    /**
     * 登陆接口
     * @param username
     * @param password
     */
    @PostMapping("login")
    public void login(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        token.setRememberMe(false);
        SecurityUtils.getSubject().login(token);
    }

    @RequiresPermissions("CLIENT:QUERY")
    @GetMapping("user/query")
    public List<UserDO> queryUser(Long page, Long pageSize, String username) {
        System.out.println("客户信息查询开始");
        System.out.println("客户信息查询结束");
        return new ArrayList<>();
    }

    @RequiresPermissions("CLIENT:UPDATE")
    @PutMapping("user/update")
    public void updateUser(UserDO user) {
        System.out.println("客户信息修改开始");
        System.out.println("客户信息修改结束");
    }

    @RequiresPermissions("CLIENT:DEL")
    @DeleteMapping("user/del")
    public void delUser(Long id) {
        System.out.println("客户信息删除开始");
        System.out.println("客户信息删除结束");
    }

    @RequiresPermissions("CLIENT:ADD")
    @PostMapping("user/add")
    public void addUser(Long id) {
        System.out.println("客户信息新增开始");
        System.out.println("客户信息新增结束");
    }
}

这里的@RequiresPermissions("xxx")注解,是用于权限验证的,有这个注解标识的,客户端请求时候就会去执行realm里的权限验证方法。

shiro配置
  • shiroConfig

    /**
     * shiro配置类
     */
    @Configuration
    public class ShiroConfig {
    
        /**
         * 核心工厂类 配置url过滤、安全管理器等信息
         * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 至上而下判断
            // url拦截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            // 以下放行
            filterChainDefinitionMap.put("/admin/login", "anon");
            filterChainDefinitionMap.put("/admin/user/query", "anon");
            filterChainDefinitionMap.put("/anon/**", "anon");
            // 其他不放行
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            // login url
            shiroFilterFactoryBean.setLoginUrl("/admin/login");
            // authc
            shiroFilterFactoryBean.getFilters().put("authc", new MyShiroFilter());
            return shiroFilterFactoryBean;
        }
    
        /**
         * 安全管理器
         * @return
         */
        @Bean
        public SecurityManager securityManager(MyShiroRealm realm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(realm);
            return securityManager;
        }
    
        /**
         * shiro域
         * @return
         */
        @Bean
        public MyShiroRealm myShiroRealm(MyCredentialMatcher matcher) {
            MyShiroRealm realm = new MyShiroRealm();
            realm.setCredentialsMatcher(matcher);
            return realm;
        }
    
        /**
         * 密码比较器
         * @return
         */
        @Bean
        public MyCredentialMatcher myCredentialMatcher() {
            return new MyCredentialMatcher();
        }
    
        /**
         * 权限验证aop注解支持
         * @param manager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager manager) {
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(manager);
            return advisor;
        }
    
        @Bean(name = "lifecycleBeanPostProcessor")
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        @Bean
        @DependsOn({ "lifecycleBeanPostProcessor" })
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            // 设置代理类
            DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
            creator.setProxyTargetClass(true);
            return creator;
        }
    }
    

    最后两个Bean是为了让项目支持shiro的aop注解。如果项目中已经引入了spring-boot-starter-aop的依赖,则可以不需要配置这两个Bean。这里是个坑,当时也研究了很久

  • MyRealm

    /**
     * shiro域——身份验证、权限验证
     */
    public class MyShiroRealm extends AuthorizingRealm {
    
        @Autowired
        private BaseService baseService;
    
        /**
         * 权限
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            UserDO user = (UserDO)principalCollection.getPrimaryPrincipal();
            List<ResourceDO> resourceDOList = baseService.selectResourceByUsername(user.getUsername());
            Set<String> collect = resourceDOList.stream().map(ResourceDO::getResId).collect(Collectors.toSet());
            authorizationInfo.setStringPermissions(collect);
            return authorizationInfo;
        }
    
        /**
         * 身份
         * @param token
         * @return
         * @throws AuthenticationException
         *
         * */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UserDO user = baseService.selectByUsername((String)token.getPrincipal());
            if (user == null) {
                throw new AuthenticationException("用户名或密码错误");
            }
            return new SimpleAuthenticationInfo(
                    user, // 用户
                    token.getCredentials(), // 表单输入的密码
                    getName() // realm name
            );
        }
    }
    
  • MyShiroFilter

    public class MyShiroFilter extends FormAuthenticationFilter {
    
        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            boolean loginRequest = this.isLoginRequest(request, response);
            HttpServletResponse res = (HttpServletResponse) response;
            String message = "未登录,请先登陆";
            res.addHeader("Content-Type", "text/html;CHARSET=UTF-8");
            res.addHeader("sessionstatus", "timeout");
            PrintWriter out = response.getWriter();
            out.write(message);
            out.close();
            return false;
        }
    }
    
  • MyCredentialMatcher

    /**
     * 密码比较器 如果密码加密方式比较简单 也可以不需要这个比较器 直接在realm里边完成验证
     */
    public class MyCredentialMatcher extends SimpleCredentialsMatcher {
    
        /**
         * @param authenticationToken
         * @param info realm里边身份验证通过后的认证信息
         * @return
         */
        @Override
        public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo info) {
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            UserDO user = (UserDO)info.getPrincipals().getPrimaryPrincipal();
            String pwdInput = new String(token.getPassword());
            // 项目中密码加密的方式
            String password = new Md5Hash(pwdInput).toHex();
            //if (!user.getPassword().equals(password)) {
                //throw new AuthenticationException("用户名或密码错误");
            //}
            return true;
        }
    }
    

按照上述配置,项目里就可以整合进shiro了。整合进shiro的主要优势,个人感觉最大的就是权限管理逻辑清晰、拓展方便

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值