springboot整合shiro进行权限管理

背景:springboot2.1,shiro1.4;由于目前的小项目没做登录,但是客户又需要加上权限,因此楼主就想到了shiro(这是单独的项目,需要集成后台管理系统)

shiro简介

Apache Shiro是Java的一个安全框架,集成相对简单,可以帮我们完成认证、授权、加密、会话管理、与Web集成、缓存等。

Authentication身份认证/登录,验证用户是不是拥有相应的身份;

Authorization授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Subject主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者;

SecurityManager安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且它管理着所有Subject;可以看出它是Shiro的核心,它负责与后边介绍的其他组件进行交互,如果学习过SpringMVC,你可以把它看成DispatcherServlet前端控制器;

Realm域,Shiro从从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源。

上面这一段是网上抄的(参考地址:https://jinnianshilongnian.iteye.com/blog/2018936

下面引入自己的实际代码

登录入口:

重新建了一个 AuthenticationToken 的子类 TenantUsernamePasswordToken 添加了一个变量tenantId

@RequestMapping("/login")
    @ResponseBody
    public GlobalResult loginUser(String loginName,String password,HttpServletRequest request) {
        AuthenticationToken usernamePasswordToken = new TenantUsernamePasswordToken(loginName,tenantId,password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(usernamePasswordToken);   //完成登录
            User user = (User) subject.getPrincipal();
            subject.getSession().setTimeout(60*60000);//1h过期(ms)
            subject.getSession().setAttribute("user", user);
            return ResultUtil.success();
        } catch (IncorrectCredentialsException e) {
            return ResultUtil.fail("密码错误");
        } catch (LockedAccountException e) {
            return ResultUtil.fail("登录失败,该用户已被冻结");
        } catch (AuthenticationException e) {
            return ResultUtil.fail("该用户不存在");
        } catch (Exception e) {
            e.printStackTrace();
            return ResultUtil.fail("登录异常,原因:{}"+e.getMessage());
        }
    }  

接下来贴出shiro主要配置

TenantUsernamePasswordToken

public class TenantUsernamePasswordToken extends UsernamePasswordToken {
    private static final long serialVersionUID = 3814343176522955308L;
    private String tenant;

    public TenantUsernamePasswordToken() {
    }

    public TenantUsernamePasswordToken(String username, String tenant, char[] password) {
        this(username, tenant, (char[])password, false, (String)null);
    }

    public TenantUsernamePasswordToken(String username, String tenant, String password) {
        this(username, tenant, (char[])(password != null?password.toCharArray():null), false, (String)null);
    }

    public TenantUsernamePasswordToken(String username, String tenant, char[] password, String host) {
        this(username, tenant, password, false, host);
    }

    public TenantUsernamePasswordToken(String username, String tenant, String password, String host) {
        this(username, tenant, password != null?password.toCharArray():null, false, host);
    }

    public TenantUsernamePasswordToken(String username, String tenant, char[] password, boolean rememberMe) {
        this(username, tenant, (char[])password, rememberMe, (String)null);
    }

    public TenantUsernamePasswordToken(String username, String tenant, String password, boolean rememberMe) {
        this(username, tenant, (char[])(password != null?password.toCharArray():null), rememberMe, (String)null);
    }

    public TenantUsernamePasswordToken(String username, String tenant, String password, boolean rememberMe, String host) {
        this(username, tenant, password != null?password.toCharArray():null, rememberMe, host);
    }

    public TenantUsernamePasswordToken(String username, String tenant, char[] password, boolean rememberMe, String host) {
        super(username, password, rememberMe, host);
        this.tenant = tenant;
    }

    public String getTenant() {
        return this.tenant;
    }

    public void setTenant(String tenant) {
        this.tenant = tenant;
    }

    public void clear() {
        super.clear();
        this.tenant = null;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.getClass().getName());
        sb.append(" - ");
        sb.append(this.getUsername());
        sb.append(", tenant=").append(this.tenant);
        sb.append(", rememberMe=").append(this.isRememberMe());
        if(this.getHost() != null) {
            sb.append(" (").append(this.getHost()).append(")");
        }

        return sb.toString();
    }
}

新定义了一个密码比较器(可根据业务需要自行配制)

public class CredentialsMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        UsernamePasswordToken utoken=(UsernamePasswordToken) token;
        //获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
        String inPassword = new String(utoken.getPassword());
        //获得数据库中的密码
        String dbPassword=(String) info.getCredentials();
        //进行密码的比对
//        return this.equals(inPassword, dbPassword);忽略密码比对
        return true;
    }

AuthRealm(进行登录校验,权限赋值等) 

public class AuthRealm extends AuthorizingRealm {

    @Autowired
    private IUserService userService;


    /**
     * 授权
     * @param principal
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        System.err.println("===================================================================================");
        if(principal == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        } else {
            User userInfo = (User)this.getAvailablePrincipal(principal);
            log.info("username:{},userId:{}", userInfo.getLoginName(), userInfo.getId());
            Set<String> permissions = new HashSet();
            List<Resource> resourceList = this.userService.findResourcesByUserId(userInfo.getId());
            if(resourceList.isEmpty()) {
                log.error("current user:{} has not resources ", userInfo.getLoginName());
                return null;
            } else {
                Iterator i$ = resourceList.iterator();
                while(i$.hasNext()) {
                    Resource resource = (Resource)i$.next();
                    String permission = resource.getCode();
                    if(StringUtil.isNotEmpty(permission) && !permissions.contains(permission)) {
                        permissions.add(permission);
                    }
                }

                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                authorizationInfo.setStringPermissions(permissions);
                return authorizationInfo;
            }
        }
    }

    /**
     * 认证登录
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        TenantUsernamePasswordToken utoken = (TenantUsernamePasswordToken) token;//获取用户输入的token
        String userName = utoken.getUsername();
        String tenantId = utoken.getTenant();
        User user;
        try {
            user = userService.getByLoginNameAndTenantId(userName,tenantId);
        } catch (Exception var8) {
            log.error("无法获取用户信息!", var8);
            throw new UnknownAccountException(SpringContextUtils.getMessage("security.usernameNotExists"), var8);
        }
        if(user == null) {
            throw new UnknownAccountException(SpringContextUtils.getMessage("security.usernameNotExists"));
        } else {
            return new SimpleAuthenticationInfo(user, null,this.getClass().getName());//放入shiro.调用CredentialsMatcher检验密码
        }
    }

} 

 ShiroConfiguration(主要配置)

@Configuration
public class ShiroConfiguration {


    /**
     * anon  匿名访问
     * authc 登陆访问
     * @param manager
     * @return
     */
    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
        ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        //配置登录的url和登录成功的url
        bean.setLoginUrl("/unlogin");
//        bean.setSuccessUrl("/merchant/home");
        //配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap=new LinkedHashMap<>();
        filterChainDefinitionMap.put("/static/**",  "anon");//静态资源不拦截
        filterChainDefinitionMap.put("/login",  "anon");
        filterChainDefinitionMap.put("/**", "authc");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

    /**
     * 配置核心安全事务管理器
     * @param authRealm
     * @return
     */
    @Bean(name="securityManager")
    public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
        System.err.println("--------------shiro已经加载----------------");
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        return manager;
    }

    /**
     * 配置自定义的权限登录器
     * @param matcher
     * @return
     */
    @Bean(name="authRealm")
    public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
        AuthRealm authRealm=new AuthRealm();
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    /**
     * 配置自定义的密码比较器
     * @return
     */
    @Bean(name="credentialsMatcher")
    public CredentialsMatcher credentialsMatcher() {
        return new CredentialsMatcher();
    }
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启shiro注解
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 开启aop
     * @param manager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(manager);
        return advisor;
    }

    /**
     * 注册全局异常处理
     * @return
     */
    @Bean(name = "exceptionHandler")
    public HandlerExceptionResolver handlerExceptionResolver() {
        return new MyExceptionHandler();
    }

}

  到此主要配置代码就已完成(主要记录方便以后自用---仅供参考)

如果想对某个接口进行权限控制需要在想要的接口上添加shiro标签(如下代码示例)

    @RequestMapping("/getUser")
    @ResponseBody
    @RequiresPermissions("/getUser")//这里是存入数据库的资源名
    public GlobalResult getUser(String loginName, String password, HttpSession session) {
        return ResultUtil.success(userService.getByLoginNameAndTenantId("zhangsan",tenantId)) ;
    }

  

 

 #####2023-6-16 18:10

这边博文比较清晰 加深记忆

https://www.cnblogs.com/laiyw/p/15121882.html

 

 

 

  

posted @ 2019-06-22 12:06  不二尘  阅读(340)  评论(0编辑  收藏  举报