spring-boot:spring-boot2.0整合shiro

shiro和security

说实话,这两个框架我并没有深入研究过,只是简单使用了他们的功能,这里选择了shiro,只要有两个方面:

  1. 配置简单,只需简单添加依赖,实现简单的认证方式即可。
  2. 轻量级,他的功能虽然没有security完善,但是完全满足一般开发情况的权限需求。

配置

添加依赖

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

这里说明一下为什么添加spring-boot-starter-aopshiro权限控制有两种方式:注解和url配置,如果想使用注解,必须加spring-boot-starter-aop,不如注解无效,原因是因为url配置是使用filter实现的,注解是使用切面实现的。

实现realm

/**
 * @author Ouyang
 * @date 18/12/13 23:30
 */
public class MyRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //null usernames are invalid
        if (principalCollection == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        ShiroUser shiroUser = new ShiroUser();
        shiroUser.setUsername("admin");
        shiroUser.setPwd(HexUtil.encryption("password"));
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, shiroUser.getPwd(), getName());
        return info;
    }

}

实际情况中ShiroUser应该从数据库查取,我是为了方便,此处自己new了一个对象。

shiroConfig

@Configuration
public class ShiroConfig {

    @Bean
    public Realm realm() {
        return new MyRealm();
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole注解,会导致该方法无法映射请求,导致返回404。
         * 加入这项配置能解决这个bug
         */
        creator.setUsePrefix(true);
        return creator;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        // all paths are managed via annotations
        chain.addPathDefinition("/**", "anon");
        return chain;
    }

}

chain.addPathDefinition("/**", "anon");表示全部请求都无需权限,如果想要,加权限,可以将anon改成authc

全局异常处理

package com.ouyanglol.demo.config.expection;

import com.ouyanglol.demo.entity.Result;
import com.ouyanglol.demo.entity.ResultException;
import com.ouyanglol.demo.entity.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

/**
 * 统一异常处理
 * @author Ouyang
 */
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ShiroException.class)
    public Result handleShiroException(ShiroException e) {
        String eName = e.getClass().getSimpleName();
        log.error("shiro执行出错:{}",eName);
        return Result.error(ResultStatus.ERROR);
    }

    @ExceptionHandler(UnauthenticatedException.class)
    public Result page401(UnauthenticatedException e) {
        log.error("未经授权:{}",e.getMessage());
        return Result.error(ResultStatus.UNAUTHORIZED);
    }

    @ExceptionHandler(UnauthorizedException.class)
    public Result page403(UnauthorizedException e) {
        log.error("无权限访问:{}",e.getMessage());
        return Result.error(ResultStatus.FORBIDDEN);
    }

    @ExceptionHandler(ResultException.class)
    public Result error(ResultException e) {
        log.error("无权限访问:{}",e.getMessage());
        return Result.error(e.getStatus());
    }

    @ExceptionHandler(Exception.class)
    public Result error(Exception e) {
        log.error("系统异常:{}",e.getMessage());
        return Result.error(ResultStatus.ERROR);
    }

}

这里需要注意一下,使用注解的方式是可以GlobalExceptionHandler才能拦截,如果是url配置的方式,会直接重定向到登陆页。

登陆测试

package com.ouyanglol.demo.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ouyanglol.demo.entity.Result;
import com.ouyanglol.demo.entity.ResultStatus;
import com.ouyanglol.demo.entity.ShiroUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author Ouyang
 * @date 18/12/16 19:16
 */
@RestController
@Slf4j
public class ShiroController {

    @RequestMapping("/401")
    public Result page401() {
        throw new UnauthenticatedException();
    }

    @RequestMapping("/403")
    public Result page403() {
        throw new UnauthorizedException();
    }

    @PostMapping("/login")
    public Object login(@RequestBody String body){
        String oper = "用户登录";
        log.info("{}, body:{}",oper,body);
        JSONObject json = JSON.parseObject(body);
        String uname = json.getString("uname");
        String pwd = json.getString("pwd");

        if (StringUtils.isEmpty(uname)){
            return Result.error(ResultStatus.FORBIDDEN);
        }
        if (StringUtils.isEmpty(pwd)){
            return Result.error(ResultStatus.FORBIDDEN);
        }

        Subject currentUser = SecurityUtils.getSubject();
        try {
            //登录
            currentUser.login( new UsernamePasswordToken(uname, pwd) );
            //从session取出用户信息
            ShiroUser user = (ShiroUser) currentUser.getPrincipal();
            if (user==null) {
                throw new AuthenticationException();
            }
            //返回登录用户的信息给前台,含用户的所有角色和权限
            return Result.success("登陆成功");
        } catch ( UnknownAccountException uae ) {
            log.warn("用户帐号不正确");
        } catch ( IncorrectCredentialsException ice ) {
            log.warn("用户密码不正确");
        } catch ( LockedAccountException lae ) {
            log.warn("用户帐号被锁定");
        } catch ( AuthenticationException ae ) {
            log.warn("登录出错");
        }
        return Result.error(ResultStatus.ERROR);

    }
}

这里只列举了简单的代码,完整代码请下载项目查看。

项目地址

地址:https://github.com/a252937166/spring-boot-demo
分支:feature/shiro

参考文章

我对shiro也是初学者水平,就不多讲,免得误人子弟,基本是按照下方文章配置的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值