shiro

shiro

Posted by z9961 on April 20, 2019

对比了shiro和spring security之后选择使用shiro作为权限验证的框架

1.在pom中添加
<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.4.0</version>
</dependency>

开始时没注意写成了shiro-spring-boot-starter

在运行的时候会报

No SecurityManager accessible to the calling code
2.创建shiro的配置文件

因为之前定接口的时候没有对用户类型进行划分,所以使用注解的方式配置controller


/*
 * shiro配置
 * */
@Configuration
public class ShiroConfig {

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
                new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        return securityManager;
    }

    //注入自定义的realm
    @Bean
    public Realm realm() {
        return new CustomRealm();
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        //验证由注解完成,这里全部放行
        chain.addPathDefinition("/**", "anon");
        return chain;
    }
}
3.其中的CustomRealm是需要自己实现的用户和权限获取方法

/*
 * 用于shiro的权限认证实现
 * */
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private IUsersService usersService;

    /**
     * 获取身份验证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) {
        // 获取用户输入的用户名和密码
        String phone = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        // 通过用户名到数据库查询用户信息
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("phone", phone);
        Users user = usersService.getOne(queryWrapper);

        if (user == null) {
            throw new UnknownAccountException();
        }
        if (!password.equals(user.getPwd())) {
            throw new IncorrectCredentialsException();
        }
        if (user.getFlag().equals(1)) {
            throw new LockedAccountException();
        }
        return new SimpleAuthenticationInfo(user, password, getName());
    }

    /**
     * 获取授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Users user = (Users) SecurityUtils.getSubject().getPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        String role = user.getType().equals(1) ? "teacher" : "student";

        System.out.println("role = " + role);
        Set<String> set = new HashSet<>();
        set.add(role);

        //设置该用户拥有的角色
        info.setStringPermissions(set);
        return info;
    }
}
4.这其中获取的数据来自于login中调用
Subject subject = SecurityUtils.getSubject();
//在认证提交前准备 token(令牌)
UsernamePasswordToken token = new UsernamePasswordToken(phone, password);
//执行认证登陆
subject.login(token);
5.最后添加全局的异常捕获处理

/*
 * shiro的全局异常捕获处理
 * */
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(ShiroException.class)
    @ResponseBody
    public Map<String, String> handleShiroException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "鉴权或授权过程出错");
        return map;
    }

    @ExceptionHandler(UnauthenticatedException.class)
    @ResponseBody
    public Map<String, String> UnauthenticatedException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "认证失败");
        return map;
    }

    @ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public Map<String, String> UnauthorizedException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "授权失败");
        return map;
    }

    @ExceptionHandler(LockedAccountException.class)
    @ResponseBody
    public Map<String, String> LockedAccountException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "该账号已被禁用");
        return map;
    }

    @ExceptionHandler(IncorrectCredentialsException.class)
    @ResponseBody
    public Map<String, String> IncorrectCredentialsException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "用户名或密码错误");
        return map;
    }

    @ExceptionHandler(UnknownAccountException.class)
    @ResponseBody
    public Map<String, String> UnknownAccountException(HttpServletResponse response) {
        Map<String, String> map = get401Map(response);
        map.put("msg", "未注册的用户");
        return map;
    }


    private Map<String, String> get401Map(HttpServletResponse response) {
        response.setStatus(401);
        Map<String, String> map = generalMethod.getErrorMap();
        map.put("state", "401");
        return map;
    }
}