springboot集成shiro

  1. 云栖社区>
  2. 博客>
  3. 正文

springboot集成shiro

小尘哥 2018-04-28 15:35:00 浏览573
展开阅读全文

废话少说,先简单说下怎么用,至于shiro是什么,请移步shiro官网

1、添加依赖

            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>

            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>${shiro.version}</version>
            </dependency>

            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>

            <dependency>
                <groupId>net.mingsoft</groupId>
                <artifactId>shiro-freemarker-tags</artifactId>
                <version>${shiro-freemarker.version}</version>
            </dependency>

2、重写AuthorizingRealm

AuthorizingRealm主要包括两个方法:doGetAuthorizationInfo(PrincipalCollection principals)和doGetAuthenticationInfo(AuthenticationToken token)
1.doGetAuthenticationInfo(AuthenticationToken token)在登录认证时候被调用,即SecurityUtils.getSubject().login()执行时候被调用
2.doGetAuthorizationInfo(PrincipalCollection principals)获取权限认证信息,即SecurityUtils.getSubject().isPermitted()执行时候被调用
我这里定义成abstract 是为了提取该类为公用,其他项目可以复用集成。

public abstract class AbstarctAuthorizingRealm extends AuthorizingRealm {

    @Resource
    private IUserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        Object primaryPrincipal = principals.getPrimaryPrincipal();
        if (primaryPrincipal == null) {
            return null;
        }

        IUser user = (IUser) primaryPrincipal;

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(findPermissions(user));
        info.setRoles(findRoles(user));

        return info;
    }

  
      /**
      * 获取用户的权限集合
      */
    protected abstract Set<String> findPermissions(IUser user);
    /**
      * 获取用户的角色集合
      */
    protected abstract Set<String> findRoles(IUser user);

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upt = (UsernamePasswordToken) token;
        String username = upt.getUsername();
        char[] password = upt.getPassword();
        if (StringUtils.isBlank(username)) {
            throw new MustUsernameException();
        }

        if (password == null || password.length == 0) {
            throw new MustPasswordException();
        }

        IUser user = userService.findUserByUsername(username);
        if (user == null) {
            throw new UnknownAccountException();
        }

        if (Boolean.TRUE.equals(user.getLocked())) {
            throw new LockedAccountException();
        }

        if (Boolean.TRUE.equals(user.getDisabled())) {
            throw new DisabledAccountException();
        }

        AuthenticationInfo authenticationInfo = assertAuthenticationInfo(user);

        return authenticationInfo;
    }

    protected AuthenticationInfo assertAuthenticationInfo(IUser user) {
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
        return authenticationInfo;
    }

    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)
            throws AuthenticationException {
        CredentialsMatcher cm = getCredentialsMatcher();
        if (cm != null) {
            if (!cm.doCredentialsMatch(token, info)) {
                throw new IncorrectCredentialsException();
            }
        } else {
            throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify "
                    + "credentials during authentication.  If you do not wish for credentials to be examined, you "
                    + "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
        }
    }

    public IUserService getUserService() {
        return userService;
    }

    public void setUserService(IUserService userService) {
        this.userService = userService;
    }

IUser也是一个接口,包括用户基本登录信息,方便每个用到的项目自己做实现

public interface IUser {

    String getUsername();

    String getPassword();

    Boolean getDisabled();

    Integer getDeleted();

    Boolean getLocked();

}

3、登录成功回调(重写FormAuthenticationFilter)

比如登录成功后需要修改用户最后登录时间,登录ip,记录日志等等操作都可以在这里进行。

public class KaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
    
    final Logger logger = LoggerFactory.getLogger(getClass());
    
    private MessageSourceAccessor messageSourceAccessor;
    
    public KaptchaFormAuthenticationFilter(MessageSourceAccessor messageSourceAccessor) {
        this.messageSourceAccessor = messageSourceAccessor;
    }

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue)
            throws Exception {
        if (request.getAttribute(getFailureKeyAttribute()) != null) {
            return true;
        }
        return super.onAccessDenied(request, response, mappedValue);
    }

    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
            ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))) {
            httpServletResponse.setCharacterEncoding("UTF-8");
            PrintWriter out = null;
            try{
                out = httpServletResponse.getWriter();
                out.println(FastJsonUtils.toJSONString(ResultModel.defaultSuccess(null)));
                out.flush();
            }finally{
                if(out != null){
                    out.close();
                }
            }
            return false;
        }
        
        return super.onLoginSuccess(token, subject, request, response);
        
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))) {
            String className = e.getClass().getName();
            ResultModel rm = new ResultModel(ResultStatus.FAIL.getCode(), messageSourceAccessor.getMessage(className));
            PrintWriter out = null;
            try {
                out = httpServletResponse.getWriter();
                out.println(FastJsonUtils.toJSONString(rm));
                out.flush();
            } catch (IOException e1) {
                if(logger.isInfoEnabled()){
                    e1.printStackTrace();
                }
            }finally{
                if(out != null){
                    out.close();
                }
            }
            return false;
        }
        return super.onLoginFailure(token, e, request, response);
    }

    public MessageSourceAccessor getMessageSourceAccessor() {
        return messageSourceAccessor;
    }

    public void setMessageSourceAccessor(MessageSourceAccessor messageSourceAccessor) {
        this.messageSourceAccessor = messageSourceAccessor;
    }

4、springboot配置shiroConfig

package com.mos.easyboot.admin.config;

import com.google.common.collect.Maps;
import com.mos.easyboot.admin.config.service.impl.PermissionService;
import com.mos.easyboot.admin.config.service.impl.UserService;
import com.mos.easyboot.tools.shiro.KaptchaValidateFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.support.MessageSourceAccessor;

import javax.servlet.Filter;
import java.util.Map;

/**
 * @author 小尘哥
 */
@Configuration
public class ShiroConfig{


    @Bean(name = "securityManager")
    public DefaultWebSecurityManager securityManager(PermissionService permissionService) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(userRealm(permissionService));
        return manager;
    }

    @Bean
    public MethodInvokingFactoryBean setSecurityManager(PermissionService permissionService) {
        MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
        methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        methodInvokingFactoryBean.setArguments(securityManager(permissionService));
        return methodInvokingFactoryBean;
    }

    @Bean
    @DependsOn(value = "lifecycleBeanPostProcessor")
    public UserRealm userRealm(PermissionService permissionService) {
        UserRealm userRealm = new UserRealm(permissionService);
        userRealm.setCredentialsMatcher(credentialsMatcher());
        return userRealm;
    }

    @Bean
    public HashedCredentialsMatcher credentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
//        credentialsMatcher.setHashIterations(2); MD5加密迭代次数1:MD5(str),2:MD5(MD5(str))
        return credentialsMatcher;
    }

    @Bean
    public FormAuthenticationFilter authcFilter(@Qualifier("messageSourceAccessor") MessageSourceAccessor messageSourceAccessor,
                                                @Qualifier("userService") UserService userService) {
        CustomFormAuthenticationFilter authenticationFilter = new CustomFormAuthenticationFilter(messageSourceAccessor,userService);
        authenticationFilter.setLoginUrl("/login");
        return authenticationFilter;
    }

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

    public LogoutFilter logoutFilter() {
        LogoutFilter logout = new LogoutFilter();
        logout.setRedirectUrl("/login");
        return logout;
    }
    @Bean
    public KaptchaValidateFilter kaptchaValidate() {
        return new KaptchaValidateFilter();
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("messageSourceAccessor") MessageSourceAccessor messageSourceAccessor,
                                              @Qualifier("userService") UserService userService,
                                              @Qualifier("permissionService") PermissionService permissionService) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager(permissionService));
        bean.setLoginUrl("/toLogin");
        bean.setUnauthorizedUrl("/unauthor");
        bean.setSuccessUrl("/index");

        Map<String, Filter> filters = Maps.newHashMap();
        filters.put("kaptchaValidate", kaptchaValidate());
        filters.put("authc", authcFilter(messageSourceAccessor,userService));
        filters.put("logout", logoutFilter());
        bean.setFilters(filters);

        Map<String, String> filterChainDefinitionMap = Maps.newHashMap();
        filterChainDefinitionMap.put("/toLogin", "anon");
        filterChainDefinitionMap.put("/login*", "kaptchaValidate,authc");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/demo/**", "anon");
        filterChainDefinitionMap.put("/kaptcha.jpg", "anon");
        filterChainDefinitionMap.put("/app/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/plugins/**", "anon");
        filterChainDefinitionMap.put("/**", "user");

        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return bean;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public MessageSourceAccessor messageSourceAccessor(@Qualifier("messageSource") MessageSource messageSource){
        return new MessageSourceAccessor(messageSource);
    }
}

5、添加shiro拦截

    @Bean
    public FilterRegistrationBean shiroFilterRegistration() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
        return filterRegistration;
    }

6、使用

controller直接加@RequiresPermissions("对应的权限")或@RequiresRoles("对应的角色")即可
模板上:<@shiro.hasPermission name="你定义的权限标识">/@shiro.hasPermission

7、其他

有些代码没有贴出来,随后我会把整个项目开源出来,easy-boot,基于springboot的快速开发框架搭建方案,希望大家多多支(tu)持(cao)····

网友评论

登录后评论
0/500
评论
小尘哥
+ 关注